import {
    longCodeTextToMetadataLanguage,
    shortCodeTextToMetadataLanguage
} from "../../common/languages";
import {
    Func,
    LocalLanguage,
    MapLike,
    Mutable,
    ScriptOptions,
    ScriptOptionUpdateParam
} from "../../common/types";
import { findArray, getMapEntries, stringEqual } from "../../common/util";
import {
    Diagnostic,
    DiagnosticMessage,
    errorMessages,
    warningMessages
} from "../diagnostic";
import { basicTypes, objectAllocator } from "./impl";
import { isLigalIdentifier } from "./scanner";
import * as t from "./types";
import {
    createTextRange,
    forEachSourceFileInclude,
    isNode,
    isNodeArray,
    isSymbolTable,
    mergeSymbolMapTo,
    metadataFieldFlagToString,
    removeStringQuotation,
    stringToLabelType,
    stringToUserContext,
} from "./util";

const iterableInterface = "IEnumerable";
const thisPropertyName = "me";

const metadataUsingTypeNames = {
    category: "MDM.IElement",
    categories: "MDM.IElements",
    field: "DOM.IQuestion",
    style: "MDM.IStyle",
    controlStyle: "MDM.IControlStyle",
    fontStyle: "MDM.IFontStyle",
    cellStyle: "MDM.ICellStyle"
};

const sectionMemberNameSets = {
    job: new Set(t.sectionMemberNames.job.map(e => e.toLowerCase())),
    inputDataSource: new Set(t.sectionMemberNames.inputDataSource.map(e => e.toLowerCase())),
    outputDataSource: new Set(t.sectionMemberNames.outputDataSource.map(e => e.toLowerCase())),
    globalSQLVariables: new Set(t.sectionMemberNames.globalSQLVariables.map(e => e.toLowerCase())),
    logging: new Set(t.sectionMemberNames.logging.map(e => e.toLowerCase())),
};

const stringToBasicTypesObject: MapLike<t.IntrinsicType> = {
    any: basicTypes.any,
    string: basicTypes.string,
    boolean: basicTypes.boolean,
    null: basicTypes.null,
    long: basicTypes.long,
    double: basicTypes.double,
    categorical: basicTypes.categorical,
    object: basicTypes.object,
    date: basicTypes.date,
};

const stringToBasicTypes = new Map(getMapEntries(stringToBasicTypesObject));

function textToBasicTypes(text: string): t.IntrinsicType | undefined {
    return stringToBasicTypes.get(text.toLowerCase());
}

function getTypeFullName(type: t.IntrinsicType | t.Type): string {
    let name = "";
    if (type.isType()) {
        let symbol = type.symbol;
        let nameStack: string[] = [ symbol.name ];
        while (symbol.parent) {
            symbol = symbol.parent;
            nameStack.push(symbol.name);
        }
        name = nameStack.reverse().join('.');
    }
    else {
        name = type.name;
    }
    if ((type.flag & t.TypeFlag.arrayType) > 0) {
        name += "[]".repeat(type.rank ?? 1);
    }
    return name;
}

function getModifierCheckFlag(signature: t.PropertySignature): t.CheckFlag {
    let result = t.CheckFlag.none;
    if (signature.modifiers) {
        signature.modifiers.forEach(m => {
            if (m.kind === t.SyntaxKind.readonlyKeyword) {
                result |= t.CheckFlag.readonly;
            }
            else if (m.kind === t.SyntaxKind.writeonlyKeyword) {
                result |= t.CheckFlag.writeonly;
            }
        });
    }
    return result;
}

function getParameterCheckFlag(signature: t.ParameterSignature): t.CheckFlag {
    let result = t.CheckFlag.none;
    if (signature.paramArray) {
        result |= t.CheckFlag.paramArray;
    }
    if (signature.optional) {
        result |= t.CheckFlag.optional;
    }
    if (signature.declaration?.isDeclaration<t.ArrayDecl>(t.SyntaxKind.arrayDecl)) {
        result |= t.CheckFlag.readonly;
    }
    return result;
}

function getModuleSignature(symbol?: t.Symbol): t.ModuleSignature | undefined {
    if (!symbol || !symbol.declaration) {
        return undefined;
    }
    if (symbol.declaration.isSignature<t.ModuleSignature>(t.SyntaxKind.moduleSignature)) {
        return symbol.declaration;
    }
    else if (symbol.declaration.isSignature<t.ClassSignature>(t.SyntaxKind.classSignature) || symbol.declaration.isSignature<t.InterfaceSignature>(t.SyntaxKind.interfaceSignature)) {
        return symbol.declaration.module;
    }
    return undefined;
}

function getSymbolFromMap(map: t.ReadonlySymbolMap | undefined, name: string, callback?: Func<t.Symbol | undefined, void>): t.Symbol | undefined;
function getSymbolFromMap(map: t.ReadonlySymbolMap | undefined, names: string[], callback?: Func<t.Symbol | undefined, void>): t.Symbol | undefined;
function getSymbolFromMap(map: t.ReadonlySymbolMap | undefined, arg: string | string[], callback?: Func<t.Symbol | undefined, void>): t.Symbol | undefined {
    if (!map) {
        return undefined;
    }
    if (typeof arg === "string") {
        let result = map.get(arg) || map.get(arg.toLowerCase());
        callback?.(result);
        return result;
    }
    let result: t.Symbol | undefined;
    if (arg.length > 0) {
        result = map.get(arg[0].toLowerCase());
        callback?.(result);
        let index = 1;
        while (result && index < arg.length) {
            let lower = arg[index].toLowerCase();
            result = result.members.get(lower) || result.locals.get(lower);
            callback?.(result);
            index++;
        }
    }
    return result;
}

interface SymbolConflictParams {
    newSymbol: t.Symbol;
    existSymbol: t.Symbol;
    name: t.Identifier;
}

type SymbolConflictCallback  = Func<SymbolConflictParams, void>;

/**
 * 将现有符号声明到已有的符号表中
 * @param symbol 已知符号
 * @param symbolTable 声明到的符号表
 * @param conflictCallback 符号名冲突时调用的回调函数
 * @returns
 */
function declareSymbolToTable(symbol: t.Symbol, symbolTable: t.ReadonlySymbolTable, scope: t.SymbolScope, conflictCallback?: SymbolConflictCallback): void;
function declareSymbolToTable(symbol: t.Symbol, symbolTable: t.ReadonlySymbolMap, conflictCallback?: SymbolConflictCallback): void;
function declareSymbolToTable(symbol: t.Symbol, arg1: t.ReadonlySymbolTableLike, arg2?: t.SymbolScope | SymbolConflictCallback, arg3?: SymbolConflictCallback) {
    let exist: t.Symbol | undefined;
    let cb: SymbolConflictCallback | undefined;

    if (isSymbolTable(arg1)) {
        exist = arg1.get(symbol.escapedName, arg2 as t.SymbolScope);
        cb = arg3;
    }
    else {
        exist = arg1.get(symbol.escapedName);
        cb = arg2 as SymbolConflictCallback;
    }

    if (exist && cb) {
        cb({
            newSymbol: symbol,
            existSymbol: exist,
            name: symbol.declaration.name
        });
        return;
    }
    if (isSymbolTable(arg1)) {
        (arg1 as t.SymbolTable).set(symbol, arg2 as t.SymbolScope);
    }
    else {
        (arg1 as Map<string, t.Symbol>).set(symbol.escapedName, symbol);
    }
}

function updateSymbolNode(symbol: t.Symbol | undefined, node: Mutable<t.Node>, type?: t.LocalType) {
    if (symbol) {
        if (type) {
            symbol.tracker.setNodeType(node, type);
        }
        else {
            symbol.tracker.appendNode(node);
        }
        node.symbol = symbol;
    }
}

/**
 * 判断当前类型是否继承或实现指定的接口
 * @param type 已知类型
 * @param interfaceName 检查的接口名
 * @returns
 */
function isTypeInherit(type: t.Type, interfaceName: string): boolean {
    let result = false;
    if (type.implements.length > 0) {
        for (const impl of type.implements) {
            if (result) {
                break;
            }
            if (stringEqual(impl.name, interfaceName)) {
                result = true;
            }
            // 查询多层继承
            else {
                result = isTypeInherit(impl, interfaceName);
            }
        }
    }
    if (!result && type.inherits.length > 0) {
        for (const inh of type.inherits) {
            if (result) {
                break;
            }
            if (stringEqual(inh.name, interfaceName)) {
                result = true;
            }
            else {
                result = isTypeInherit(inh, interfaceName);
            }
        }
    }
    return result;
}

/**
 * 推断当前符号的枚举类型结果
 * @param symbol 已有符号
 * @param onError 如果符号不是可枚举类型调用的回调函数
 * @returns
 */
function computeSymbolIterableType(symbol: t.Symbol, onError?: () => void): t.LocalType {
    const type = symbol.type;
    // 数组类型
    if (type.rank > 0) {
        return type.original ?? basicTypes.any;
    }
    // 对象类型
    if (type.isType() && isTypeInherit(type, iterableInterface)) {
        let defaultMember = type.symbol.defaultMember;
        if (defaultMember &&
            defaultMember.escapedName === "item" &&
            defaultMember.parameters.length === 1) {
            return defaultMember.type;
        }
        else {
            if (onError) {
                onError();
            }
        }
    }
    return basicTypes.any;
}


function createSymbolFromLiteral(literal: t.Literal, converter: t.Converter, id: number, parent?: t.Symbol): t.Symbol {
    let flag: t.SymbolKind = t.SymbolKind.literal;
    let type: t.IntrinsicType = basicTypes.any;
    switch (literal.kind) {
        case t.SyntaxKind.categoricalLiteral:
            type = basicTypes.categorical;
            break;
        case t.SyntaxKind.categoricalItemLiteral:
            type = basicTypes.categorical;
            break;
        case t.SyntaxKind.trueLiteral:
        case t.SyntaxKind.falseLiteral:
            type = basicTypes.boolean;
            break;
        case t.SyntaxKind.stringLiteral:
            type = basicTypes.string;
            break;
        case t.SyntaxKind.numberLiteral:
            const numberLiteral = literal as t.NumberLiteral;
            if (numberLiteral.literalType === t.LiteralType.number) {
                type = Number.isInteger(numberLiteral.value) ? basicTypes.long : basicTypes.double;
            }
            else {
                type = basicTypes.long;
            }
            break;
        case t.SyntaxKind.nullLiteral:
            type = basicTypes.null;
            break;
    }
    const symbol = new (objectAllocator.getSymbolConstructor())(
        id,
        literal.text,
        flag,
        converter.convertNodeToDeclaration(literal));
    symbol.tracker.setNodeType(literal, type);
    if (parent) {
        symbol.set("parent", parent);
    }
    return symbol;
}

function createSymbolFromIdentifier(identifier: t.Identifier, id: number, converter: t.Converter, flag: t.SymbolKind = t.SymbolKind.variable, parent?: t.Symbol, type?: t.LocalType): t.Symbol {
    const symbol = new (objectAllocator.getSymbolConstructor())(id,
        identifier.text,
        flag,
        converter.convertIdentifierToVariableDeclaration(identifier),
        type);
    if (parent) {
        symbol.set("parent", parent);
    }
    return symbol;
}

function createSymbolFromNode(node: t.Node, id: number, converter: t.Converter, flag: t.SymbolKind = t.SymbolKind.variable, parent?: t.Symbol, type?: t.LocalType): t.Symbol {
    const symbol = new (objectAllocator.getSymbolConstructor())(id,
        node.text,
        flag,
        converter.convertNodeToDeclaration(node),
        type);
    if (parent) {
        symbol.set("parent", parent);
    }
    return symbol;
}

function checkInheritsOrImplementsDuplication(currentType: t.LocalType, checkType: t.LocalType, inherits: boolean): boolean {
    let types = currentType.isType() ? (inherits ? currentType.inherits : currentType.implements) : undefined;
    if (!types) {
        return false;
    }
    return types.find(t => checkType === t) !== undefined;
}

function checkDMSSectionMember<T extends t.Section>(node: T, callback: (member: t.AssignmentExpression | undefined) => void) {
    if (node.is<t.JobSection>(t.SyntaxKind.jobSection)) {
        callback(node.tempDirectory);
    }
    else if (node.is<t.InputDataSourceSection>(t.SyntaxKind.inputDataSourceSection)) {
        callback(node.connectionString);
        callback(node.selectQuery);
        callback(node.updateQuery);
        callback(node.useInputAsOutput);
        callback(node.joinKey);
        callback(node.joinKeySorted);
        callback(node.joinType);
    }
    else if (node.is<t.OutputDataSourceSection>(t.SyntaxKind.outputDataSourceSection)) {
        callback(node.connectionString);
        callback(node.updateQuery);
        callback(node.metadataDataOutputName);
        callback(node.tableOutputName);
        callback(node.variableOrder);
    }
    else if (node.is<t.GlobalSQLVariablesSection>(t.SyntaxKind.globalSQLVariablesSection)) {
        callback(node.connectionString);
        callback(node.selectQuery);
    }
    else if (node.is<t.LoggingSection>(t.SyntaxKind.loggingSection)) {
        callback(node.group);
        callback(node.path);
        callback(node.alias);
        callback(node.fileSize);
    }
}

function tryGetMetadataValue(value: t.Literal | t.Identifier): string | number | boolean {
    return value.isIdentifier() || value.isStringLiteral() ? `"${value.text}"`
        : value.isNumberLiteral() ? value.value
        : value.isTrueLiteral() ? true
        : value.isFalseLiteral() ? false
        : value.text;
}

function mergeMetadataFields(baseField: t.Symbol, joinField: t.Symbol, inCompatibleCallback: (newDec: t.MDMField, existDec: t.MDMField) => void) {
    let baseDec = baseField.declaration;
    let joinDec = joinField.declaration;
    if (!baseDec.isMDMField() || !joinDec.isMDMField()) {
        return;
    }
    if (baseDec.fieldFlag !== joinDec.fieldFlag) {
        inCompatibleCallback(joinDec, baseDec);
        return;
    }
    // 合并 Categorical 符号表
    mergeSymbolMapTo(undefined, baseField.locals as Map<string, t.Symbol>, joinField.locals);
    // 合并下级Field符号表
    joinField.members.forEach(field => {
        let exist = baseField.members.get(field.escapedName);
        if (exist) {
            mergeMetadataFields(exist, field, inCompatibleCallback);
        }
        else {
            (baseField.members as Map<string, t.Symbol>).set(field.escapedName, field);
        }
    });
}

function getMetadataFieldFullName(symbol: t.Symbol) {
    let items: string[] = [];
    let parent: t.Symbol | undefined = symbol;
    while (parent) {
        items.push(parent.name);
        parent = parent.parent;
    }
    return items.reverse().join("[..].");
}

enum ConflictType {
    inheritsInterface,
    inheritsClass,
    implementsInterface,
}

type TypeCheckerDelayedCallback = Func<t.ReadonlySymbolTableLike, void>;

interface TypeCheckerState {
    ignoreError: boolean;
    isDeclareFile: boolean;
    isBinaryExpressionLeft: boolean;
    isBinaryExpressionRight: boolean;
    isAssignmentExpressionLeft: boolean;
    isAssignmentExpressionRight: boolean;
    isFunctionDeclarationBody: boolean;
    isPreprocessorIfStatementCondition: boolean;
    isOnNextCaseEvent: boolean;
    isMetadata: boolean;
    isSectionStatement: boolean;
    isEventSection: boolean;
}

interface TypeCheckerContext {
    operator?: t.Token<t.BinaryOperatorKind>;
    expressionContext?: t.Symbol;
    indexExpressionContext?: t.Symbol;
    indexExpressionObject: boolean;
    indexExpression: boolean;
    callExpressionContext?: t.Symbol;
    callExpressionFunctionName?: string;
    callExpression: boolean;
    callExpressionCallee: boolean;
    callExpressionCalleeName: boolean;
    callExpressionParameters: boolean;
    macroFunctionParameter: boolean;
}

const contexSupportedFunctions = new Set([
    "containsany",
    "containsall",
    "containssome",
]);

// 语法类型检查需要按照一定的顺序来进行，因为函数或者类型声明可以在使用之后出现，在使用时不应该被判定为没有声明，所以需要按照下面的顺序：
// 1. 签名类型 - 常量签名、枚举类型签名、类签名、接口签名、命名空间签名
// 2. 本地函数定义
// 3. 按顺序检验本地变量定义和本地语句，包含函数体内部
//
// 语法中如下几种语句块有单独的定义域：
// + Event Section -> 各个Event Section之间符号表不互通，并且有各自的GlobalVariables区间
// + Function      -> 受GlobalVariables影响，如果配置了GlobalVariables，可以访问区间外的变量

export class TypeChecker implements t.TypeChecker {

    private _symbolCount: number;
    private _typeCount: number;

    private _globalDeclarations?: t.ReadonlySymbolMap;
    private _localDeclarations?: t.ReadonlySymbolMap;
    private _programTable?: t.SymbolTable;
    private _eventTable?: t.SymbolTable;

    // 本地程序的元数据定义，应该对应提前载入的'.mdd'文件中的定义和配置的本地全局定义文件的内容。
    // 允许不配置。
    private _localMetadataTable?: t.ReadonlySymbolMap;

    // 当前Scope使用的全局Module
    private _usingModule: t.ReadonlySymbolMap[];

    private _converter: t.Converter;
    private _state: TypeCheckerState;
    private _globalVariables: t.SymbolMap;
    private _optionFlag: t.OptionFlag;

    private _context: TypeCheckerContext;

    private _error?: Func<Diagnostic, void>;
    private _delayedCheck: TypeCheckerDelayedCallback[];

    private _options: ScriptOptions;

    // 当前程序的全局行标签
    private _programLocations: Map<string, t.Symbol>[];

    private _typeConstructor: new (id: number, flag: t.TypeFlag, name: string, symbol: t.Symbol) => t.Type;
    private _symbolConstructor: new (id: number, name: string, flag: t.SymbolKind, declaration?: t.SymbolDecl, type?: t.LocalType) => t.Symbol;

    constructor(converter: t.Converter, options: ScriptOptions) {
        this._symbolCount = 0;
        this._typeCount = 0;
        this._converter = converter;
        this._state = {
            ignoreError: false,
            isDeclareFile: false,
            isBinaryExpressionLeft: false,
            isBinaryExpressionRight: false,
            isAssignmentExpressionLeft: false,
            isAssignmentExpressionRight: false,
            isFunctionDeclarationBody: false,
            isPreprocessorIfStatementCondition: false,
            isOnNextCaseEvent: false,
            isMetadata: false,
            isSectionStatement: false,
            isEventSection: false,
        };
        this._programLocations = [];
        this._delayedCheck = [];
        this._optionFlag = t.OptionFlag.explicit;
        this._typeConstructor = objectAllocator.getTypeConstructor();
        this._symbolConstructor = objectAllocator.getSymbolConstructor();
        this._globalVariables = new Map();
        this._context = {
            callExpression: false,
            callExpressionCallee: false,
            callExpressionCalleeName: false,
            callExpressionParameters: false,
            indexExpression: false,
            indexExpressionObject: false,
            macroFunctionParameter: false,
        };
        this._usingModule = [];
        this._options = options;
    }

    public onError(handler?: Func<Diagnostic, void>) {
        this._error = handler;
    }

    private error(diagnostic: DiagnosticMessage, range: t.ReadonlyTextRange, ...args: string[]) {
        if (this._error && !this._state.ignoreError) {
            let start = range.start;
            let length = range.end - range.start;
            let message = diagnostic.template;
            if (args) {
                args.forEach((v, i) => message = message.replace("{" + i.toString() + "}", v));
            }
            const d: Diagnostic = {
                flag: diagnostic.flag,
                code: diagnostic.code,
                start,
                length,
                message,
            };
            this._error(d);
        }
    }

    public setGlobalDeclarations(scope?: t.ReadonlySymbolMap) {
        this._globalDeclarations = scope;
    }

    public setLocalDeclarations(scope?: t.ReadonlySymbolMap): void {
        this._localDeclarations = scope;
    }

    public setLocalMetadatas(scope?: t.ReadonlySymbolMap): void {
        this._localMetadataTable = scope;
    }

    public updateOptions(options: ScriptOptionUpdateParam) {
        if (options.userName) {
            this._options.userName = options.userName;
        }
        if (options.defaultMetadataContext !== undefined) {
            this._options.defaultMetadataContext = options.defaultMetadataContext;
        }
        if (options.defaultMetadataLabelType !== undefined) {
            this._options.defaultMetadataLabelType = options.defaultMetadataLabelType;
        }
        if (options.defaultMetadataLanguage !== undefined) {
            this._options.defaultMetadataLanguage = options.defaultMetadataLanguage;
        }
    }

    public createSymbol(kind: t.SymbolKind, name: string, checkFlag: t.CheckFlag, declaration: t.Declaration | t.PPDefine | t.Signature<t.SignatureKind>, type?: t.LocalType): t.Symbol {
        this._symbolCount++;
        const symbol = new this._symbolConstructor(this._symbolCount, name, kind, declaration);
        symbol.set("checkFlag", checkFlag);
        if (type) {
            symbol.tracker.setNodeType(declaration.name, type);
        }
        return symbol;
    }

    public createType(flag: t.TypeFlag, name: string, symbol: t.Symbol): t.Type {
        this._typeCount++;
        return new this._typeConstructor(this._typeCount, flag, name, symbol);
    }

    public createObjectType(flag: t.ObjectTypeFlag, name: string, symbol: t.Symbol): t.ObjectType {
        const objectType = this.createType(t.TypeFlag.object, name, symbol) as t.ObjectType;
        objectType.objectTypeFlag = flag;
        objectType.members = new Map();
        return objectType;
    }

    private saveState(): TypeCheckerState & { onError?: Func<Diagnostic, void> } {
        return {
            ignoreError: this._state.ignoreError,
            isDeclareFile: this._state.isDeclareFile,
            isOnNextCaseEvent: this._state.isOnNextCaseEvent,
            isSectionStatement: this._state.isSectionStatement,
            isAssignmentExpressionLeft: this._state.isAssignmentExpressionLeft,
            isAssignmentExpressionRight: this._state.isAssignmentExpressionRight,
            isBinaryExpressionLeft: this._state.isBinaryExpressionLeft,
            isBinaryExpressionRight: this._state.isAssignmentExpressionRight,
            isFunctionDeclarationBody: this._state.isFunctionDeclarationBody,
            isMetadata: this._state.isMetadata,
            isPreprocessorIfStatementCondition: this._state.isPreprocessorIfStatementCondition,
            isEventSection: this._state.isEventSection,
            onError: this._error,
        };
    }

    private recoveryState(state: TypeCheckerState & { onError?: Func<Diagnostic, void> }) {
        this.onError(state.onError);
        this._state.ignoreError = state.ignoreError;
        this._state.isDeclareFile = state.isDeclareFile;
        this._state.isOnNextCaseEvent = state.isOnNextCaseEvent;
        this._state.isSectionStatement = state.isSectionStatement;
        this._state.isAssignmentExpressionLeft = state.isAssignmentExpressionLeft;
        this._state.isAssignmentExpressionRight = state.isAssignmentExpressionRight;
        this._state.isBinaryExpressionLeft = state.isBinaryExpressionLeft;
        this._state.isBinaryExpressionRight = state.isBinaryExpressionRight;
        this._state.isFunctionDeclarationBody = state.isFunctionDeclarationBody;
        this._state.isMetadata = state.isMetadata;
        this._state.isPreprocessorIfStatementCondition = state.isPreprocessorIfStatementCondition;
        this._state.isEventSection = state.isEventSection;
    }

    public clear(): void {
        this._optionFlag = t.OptionFlag.explicit;
        this._globalVariables.clear();
        this._delayedCheck.splice(0);
        this._state.ignoreError = false;
        this._state.isDeclareFile = false;
        this._state.isOnNextCaseEvent = false;
        this._state.isSectionStatement = false;
        this._state.isAssignmentExpressionLeft = false;
        this._state.isAssignmentExpressionRight = false;
        this._state.isBinaryExpressionLeft = false;
        this._state.isBinaryExpressionRight = false;
        this._state.isFunctionDeclarationBody = false;
        this._state.isMetadata = false;
        this._state.isPreprocessorIfStatementCondition = false;
        this._state.isEventSection = false;
        this._context = {
            callExpression: false,
            callExpressionCallee: false,
            callExpressionCalleeName: false,
            callExpressionParameters: false,
            indexExpression: false,
            indexExpressionObject: false,
            macroFunctionParameter: false,
        };
        this._usingModule.splice(0);
    }

    private clearContext() {
        this._context = {
            callExpression: false,
            callExpressionCallee: false,
            callExpressionCalleeName: false,
            callExpressionParameters: false,
            indexExpression: false,
            indexExpressionObject: false,
            macroFunctionParameter: false,
        };
    }

    private saveContext(): TypeCheckerContext {
        return {
            operator: this._context.operator,
            expressionContext: this._context.expressionContext,
            indexExpressionContext: this._context.indexExpressionContext,
            indexExpression: this._context.indexExpression,
            indexExpressionObject: this._context.indexExpressionObject,
            callExpressionContext: this._context.callExpressionContext,
            callExpressionFunctionName: this._context.callExpressionFunctionName,
            callExpression: this._context.callExpression,
            callExpressionCallee: this._context.callExpressionCallee,
            callExpressionCalleeName: this._context.callExpressionCalleeName,
            callExpressionParameters: this._context.callExpressionParameters,
            macroFunctionParameter: this._context.macroFunctionParameter,
        };
    }

    private recoveryContext(context: TypeCheckerContext) {
        this._context = context;
    }

    private executeDelayedCallback(scope: t.ReadonlySymbolTableLike) {
        if (this._delayedCheck.length > 0) {
            for (const callback of this._delayedCheck) {
                callback(scope);
            }
            this._delayedCheck.splice(0);
        }
    }

    private delay(callback: TypeCheckerDelayedCallback) {
        this._delayedCheck.push(callback);
    }

    private updateCommentFlag(source: t.SourceFile) {
        if (source.commentFlag !== t.CommentType.none) {
            this._state.ignoreError = ((source.commentFlag & t.CommentType.ignoreTypeError) > 0);
        }
    }

    private declareToGlobalVariables(symbol: t.Symbol) {
        if (!this._state.isDeclareFile && !this._state.isFunctionDeclarationBody && !this._state.isEventSection) {
            this._globalVariables.set(symbol.escapedName, symbol);
        }
    }

    private usingModule(...names: string[]) {
        for (const name of names) {
            let symbol = this._globalDeclarations?.get(name.toLowerCase());
            if (symbol?.is(t.SymbolKind.module)) {
                this._usingModule.push(symbol.members);
            }
        }
    }

    private selectUsingModule(flag: t.FileKind) {
        switch (flag) {
            case t.FileKind.dms:
                this.usingModule("DOM", "MRSCRIPTCORELib");
                break;
            case t.FileKind.mrs:
                this.usingModule("TOM", "MDM", "MRSCRIPTCORELib");
                break;
            case t.FileKind.routing:
                this.usingModule("IOM", "MRSCRIPTCORELib");
                break;
        }
    }

    private tryGetSymbolFromGlobalDeclarations(name: string) {
        let symbol = this._globalDeclarations?.get(name.toLowerCase());
        if (symbol?.is(t.SymbolKind.module)) {
            return undefined;
        }
        return symbol;
    }

    private getSymbol(name: string, scope?: t.ReadonlySymbolTableLike, type?: t.SymbolScope): t.Symbol | undefined
    private getSymbol(identifier: t.Identifier, scope?: t.ReadonlySymbolTableLike, type?: t.SymbolScope): t.Symbol | undefined;
    private getSymbol(fullName: string[], scope?: t.ReadonlySymbolTableLike): t.Symbol | undefined;
    private getSymbol(arg: t.Identifier | string | string[], arg1?: t.ReadonlySymbolTableLike, type?: t.SymbolScope): t.Symbol | undefined {
        let result: t.Symbol | undefined;
        let text: string;
        if (typeof arg === "string") {
            let fullNames = arg.split(".");
            if (fullNames.length > 1) {
                return getSymbolFromMap(this._localDeclarations, fullNames)
                    || getSymbolFromMap(this._globalDeclarations, fullNames);
            }
            text = arg;
        }
        else if (Array.isArray(arg)) {
            return getSymbolFromMap(this._localDeclarations, arg)
                || getSymbolFromMap(this._globalDeclarations, arg);
        }
        else {
            text = arg.text;
        }
        // 如果在函数体内，只查找指定符号表内的符号
        // 查询Function函数体的符号表、程序函数表、程序宏表、本地声明表、全局声明表
        result = (isSymbolTable(arg1) ? arg1.get(text) : (arg1?.get(text.toLowerCase()) || arg1?.get(text)))
            || (this._state.isFunctionDeclarationBody ? this._programTable?.get(text, t.SymbolScope.macro | t.SymbolScope.function) : undefined)
            || ((this._state.isEventSection || this._state.isMetadata) ? this._programTable?.get(text, t.SymbolScope.macro) : undefined) /** 在 Event 或 Metadata 内时，需要查询程序定义的宏 */
            || (this._state.isEventSection ? this._eventTable?.get(text, this._state.isFunctionDeclarationBody ? (t.SymbolScope.macro | t.SymbolScope.function) : type) : undefined)
            || findArray(this._usingModule, e => e.get(text.toLowerCase())) // 相较全局声明表，优先查找当前使用的模块符号表，因为模块内声明允许和模块重名，此处需要避免在重名时查到模块符号的情况
            || this.tryGetSymbolFromGlobalDeclarations(text) // this._globalDeclareScope?.get(text, type)
            || this._localDeclarations?.get(text.toLowerCase());
        // 如果 Option 设置为 GlobalVariables 时， 查询全局变量表
        if (!result && !!(this._optionFlag & t.OptionFlag.globalVariables)) {
            result = this._globalVariables.get(text.toLowerCase());
        }
        // 如果在OnNextCase中，额外查找元数据对象
        if (!result &&
            (this._state.isOnNextCaseEvent || this._state.isMetadata) &&
            (type === undefined || !!(type & t.SymbolScope.metadata))) {
            result = arg1?.get(text.toLowerCase())
                || this._programTable?.get(text, t.SymbolScope.metadata)
                || this._localMetadataTable?.get(text.toLowerCase())
                ;
        }
        // 如果 Option 设置为 Implicit 时，声明本地符号
        if (!result && isNode(arg) && (this._optionFlag & t.OptionFlag.implicit) > 0) {
            result = createSymbolFromIdentifier(
                arg,
                ++this._symbolCount,
                this._converter,
                t.SymbolKind.variable
            );
            if (arg1) {
                this.declareSymbolToSymbolTableLike(result, arg1, t.SymbolScope.local);
            }
        }
        return result;
    }

    /**
     *
     * 在已知命名空间时，在符号表中搜索指定名称的符号，应当遵从如下规则：
     * + 对于类型名列表的第一个元素，首先在已知命名空间中搜索
     * + 如果在当前命名空间中没有找到，在全局符号表中搜索类型
     * 命名空间在符号表中的定义名称是其全名，即`lv1.lv2...`，检索名是小写。
     * 此方法只能又来查询声明类型。
     *
     * @param identifiers 类型名节点列表，顺序应当从命名空间开始，到类型名结束
     * @param module 已知的的模块
     * @param table 当前符号表
     * @param ignoreError 是否忽略搜索失败时的报错
     */
    private getSymbolFromModule(identifiers: t.NodeArray<t.Identifier>, module: t.ModuleSignature | undefined, scope?: t.ReadonlySymbolTableLike, ignoreError: boolean = false): t.Symbol | undefined {

        if (identifiers.length === 0 || identifiers[0].isMissingNode()) {
            return undefined;
        }

        // 因为不确定类型名是否包含当前命名空间，首先判断搜索完整名称包含指定命名空间名
        // 需要额外注意，当多级命名空间时，需要注意忽略最前面级别的命名空间名的情况：
        // 在`Lv1.Lv2.Lv3`下，如果写Lv3下的类型名，可以是：
        // + Lv1.Lv2.Lv3.Type
        // + Lv2.Lv3.Type
        // + Lv3.Type
        // + Type
        //
        // 同时应当注意，在本命名空间下，可以通过全名指定外部命名空间中的类名
        // 检索命名空间时，需要检查`ModuleSignature`的父级模块，因为类名级别需要连续，
        // `identifiers`长度大于0时，第一个值一定是命名空间名，如果找到，需要依次向后判断是否在当前多级命名空间中，
        // 如果不在，需要检查同级别是否有同名命名空间.

        // 最终的检索名一定是多级名称中的最后一个，其余的内容应当是命名空间名
        let searchName = identifiers[identifiers.length - 1];
        let aheadModuleName = "";
        let inCurrentModule = false;

        // 只有全名中包含'.'时，才检查名称中的命名空间
        if (identifiers.length > 1) {
            // 没有给定命名空间，首先尝试查找
            if (!module) {
                let find = this.getSymbol(identifiers[0], scope, t.SymbolScope.declare);
                if (find?.is(t.SymbolKind.module) && find.declaration.isSignature<t.ModuleSignature>(t.SyntaxKind.moduleSignature)) {
                    module = find.declaration;
                }
            }
            // 给定命名空间的情况
            if (module) {
                inCurrentModule = true;
                // 如果指定全名中命名空间的长度大于给定命名空间的长度，判定全名中的命名空间不在给定命名空间中
                if (module.fullName.length < identifiers.length - 1) {
                    inCurrentModule = false;
                }
                // 否则，从全名中倒数第二个标识符向前对比
                else {
                    // 从最后开始向前比较
                    // `ModuleSignature.fullName`的长度一定大于0
                    let identifiersIndex = identifiers.length - 2;
                    let moduleIndex = module.fullName.length - 1;
                    while (identifiersIndex >= 0) {
                        // 如果任意一个名称不同，视为命名空间整体不同
                        if (!stringEqual(identifiers[identifiersIndex].text, module.fullName[moduleIndex].text)) {
                            inCurrentModule = false;
                            break;
                        }
                        identifiersIndex--;
                        moduleIndex--;
                    }
                    // 检查是否有省略命名空间
                    if (inCurrentModule) {
                        if (moduleIndex > 0) {
                            aheadModuleName = module.fullName.slice(0, moduleIndex + 1).map(n => n.text).join(".");
                        }
                    }
                }
            }
        }

        let symbol: t.Symbol | undefined;
        let fullModule: string = "";

        // 通过命名空间名检查时，只在命名空间的成员表中查找
        // 如果给定全名长度为1：
        // + 如果给定`module`参数，则只在给定的命名空间中查找
        // + 如果没有给定`module`参数，则在全局查找
        // 如果全名长度大于1，当时经检查，全名在给定命名空间中：
        // + 在给定命名空间中查找`identifiers`中最后一个的名称
        if (identifiers.length === 1) {
            symbol = module ?
                (module.name.symbol?.members?.get(searchName.text.toLowerCase()) ?? this.getSymbol(searchName.text, scope, t.SymbolScope.declare)) :
                this.getSymbol(searchName.text.toLowerCase(), scope, t.SymbolScope.declare);
        }
        // 给定全名在给定命名空间中时，只查找命名空间成员
        else if (inCurrentModule) {
            // 更新命名空间节点符号
            fullModule = aheadModuleName;
            for (let i = 0; i < identifiers.length - 1; i++) {
                fullModule += (fullModule.length > 0 ? "." : "") + identifiers[i].text;
                let moduleSymbol = this.getSymbol(fullModule, scope, t.SymbolScope.declare);
                if (moduleSymbol && (moduleSymbol.kind & t.SymbolKind.module) > 0) {
                    updateSymbolNode(moduleSymbol, identifiers[i]);
                }
            }
            symbol = module!.name.symbol?.members?.get(searchName.text.toLowerCase());
        }
        // 不重合且`identifiers`的长度大于1时，需要首先检索命名空间
        else {
            fullModule = "";
            let finalModuleSymbol: t.Symbol | undefined;
            for (let i = 0; i < identifiers.length - 1; i++) {
                fullModule += (fullModule.length > 0 ? "." : "") + identifiers[i].text;
                // 查找命名空间
                finalModuleSymbol = this.getSymbol(fullModule, scope, t.SymbolScope.declare);
                if (finalModuleSymbol && (finalModuleSymbol.kind & t.SymbolKind.module) > 0) {
                    updateSymbolNode(finalModuleSymbol, identifiers[i]);
                }
            }
            symbol = finalModuleSymbol?.members?.get(searchName.text.toLowerCase());
        }

        if (!symbol) {
            if (!ignoreError) {
                if (identifiers.length === 1) {
                    this.error(errorMessages.typeIsNotExist, searchName, searchName.text);
                }
                else {
                    this.error(errorMessages.typeIsNotExistInModule, searchName, fullModule, searchName.text);
                }
            }
        }
        else {
            updateSymbolNode(symbol, searchName);
        }

        return symbol;
    }

    private declareSymbolToSymbolTableLike(symbol: t.Symbol, scope: t.ReadonlySymbolTableLike, scopeType: t.SymbolScope, arg?: DiagnosticMessage | SymbolConflictCallback) {
        let callback: SymbolConflictCallback | undefined = arg ? (typeof arg === "function" ? arg : (param => this.error(arg, param.name, param.name.text))) : undefined;
        if (isSymbolTable(scope)) {
            declareSymbolToTable(symbol, scope, scopeType, callback);
        }
        else {
            declareSymbolToTable(symbol, scope, callback);
        }
    }

    private checkMetadataFieldCategoriesCoincidence(symbol: t.Symbol): boolean {
        if (!this._context.expressionContext || symbol.escapedName === this._context.expressionContext.escapedName) {
            return true;
        }
        symbol.locals.forEach(cat => {
            if (!this._context.expressionContext!.locals.has(cat.escapedName)) {
                return false;
            }
        }, this);
        return true;
    }

    private computeCategoricalItemBoundary(bound: t.Identifier | t.NumberLiteral | undefined, existNamesSet: Set<string>) {
        if (!bound || bound.isNumberLiteral()) {
            return;
        }

        if (existNamesSet.has(bound.text.toLowerCase())) {
            this.error(
                warningMessages.duplicateCategoryValue,
                bound,
                bound.text
            );
        }
        else {
            existNamesSet.add(bound.text.toLowerCase());
        }

        if (this._context.operator &&
            this._context.operator.kind !== t.SyntaxKind.plus &&
            this._context.operator.kind !== t.SyntaxKind.equal
        ) {
            return;
        }

        let symbol = this._context.expressionContext;
        if (this._context.indexExpression) {
            symbol = this._context.indexExpressionContext;
        }
        else if (this._context.callExpression) {
            symbol = this._context.callExpressionContext;
        }
        if (symbol) {
            const exist = symbol.locals.get(bound.text.toLowerCase());
            if (exist) {
                updateSymbolNode(exist, bound);
            }
            else {
                if (this._context.callExpressionFunctionName === "containsall") {
                    this.error(
                        warningMessages.containsAllWillAlwaysReturnValue,
                        bound,
                        bound.text,
                        getMetadataFieldFullName(symbol),
                        "false"
                    );
                }
                else {
                    this.error(
                        warningMessages.categoryIsNotDefinedInField,
                        bound,
                        bound.text,
                        getMetadataFieldFullName(symbol)
                    );
                }
            }
        }
    }

    private computeCategoricalItemLiteral(literal: t.CategoricalItemLiteral, existNamesSet: Set<string>) {
        this.computeCategoricalItemBoundary(literal.upper, existNamesSet);
        this.computeCategoricalItemBoundary(literal.lower, existNamesSet);
    }

    private computeIdentifier(identifier: t.Identifier, scope: t.ReadonlySymbolTableLike, allowModule: boolean): t.Symbol {
        // missing node
        if (identifier.isMissingNode()) {
            return createSymbolFromIdentifier(identifier, ++this._symbolCount, this._converter);
        }
        // normal
        let symbol: t.Symbol | undefined;
        // #if
        if (this._state.isPreprocessorIfStatementCondition) {
            // defined
            if (identifier.text === "defined") {
                symbol = createSymbolFromIdentifier(
                    identifier,
                    ++this._symbolCount,
                    this._converter,
                    t.SymbolKind.macroFunction,
                    undefined,
                    basicTypes.boolean
                );
                symbol.parameters.push(
                    this.createSymbol(
                        t.SymbolKind.macro,
                        "MACRO_NAME",
                        t.CheckFlag.none,
                        this._converter.convertNodeToDeclaration(this._converter.factory.createNode(t.SyntaxKind.identifier)),
                        basicTypes.any
                    )
                );
                return symbol;
            }
            symbol = this.getSymbol(identifier, scope, t.SymbolScope.macro);
            if (!symbol) {
                this.error(errorMessages.macroIsNotDeclared, identifier, identifier.text);
            }
        }
        else {
            symbol = this.getSymbol(identifier, scope);
            if (!symbol) {
                let message = errorMessages.variableIsNotDeclared;
                if (this._context.callExpressionCalleeName) {
                    message = errorMessages.functionIsNotDeclared;
                }
                this.error(message, identifier, identifier.text);
            }
        }
        // 如果未找到符号，返回为未声明符号
        if (!symbol || (symbol.is(t.SymbolKind.module) && !allowModule)) {
            symbol = createSymbolFromIdentifier(
                identifier,
                ++this._symbolCount,
                this._converter,
                t.SymbolKind.variable | t.SymbolKind.undefined
            );
        }
        else {
            updateSymbolNode(symbol, identifier);
            if (symbol.isMDM(t.SymbolMDMKind.field)) {
                if (!this._context.expressionContext) {
                    this._context.expressionContext = symbol;
                }
                if (this._context.callExpression) {
                    this._context.callExpressionContext = symbol;
                }
            }
        }
        return symbol;
    }

    private computeIndexExpression(node: t.IndexExpr, scope: t.ReadonlySymbolTableLike): t.Symbol {
        let symbol: t.Symbol | undefined;
        let object = node.object;
        let indices: t.IndexExprIndex[] = [node.index];
        // context
        let inIndexExpression = this._context.indexExpression;
        this._context.indexExpression = true;
        // 检查多级索引
        while (object.isExpression<t.IndexExpr>(t.SyntaxKind.indexExpr)) {
            // 记录多级索引
            indices.push(object.index);
            object = object.object;
        }
        let finalName: t.Identifier;
        // 分两种情况：identifier[index] 或 object.member[index]
        // 语法上不允许: callee(...)[index] 这种形式
        if (object.isIdentifier()) {
            finalName = object;
            symbol = this.computeIdentifier(object, scope, false);
        }
        else {
            finalName = object.member;
            symbol = this.computeMemberExpression(object, scope);
        }
        // 对于找到符号的情况，检查找到的符号类型是否登记索引以及索引类型是否兼容
        // 如果没有登记索引，尝试获取是否有等级索引的默认属性
        if (symbol && !symbol.is(t.SymbolKind.metadata) && !symbol.isCheckFlag(t.CheckFlag.indexable)) {
            let defaultProperty = symbol.getMaybeDefaultProperty(s => s.isCheckFlag(t.CheckFlag.indexable));
            if (defaultProperty) {
                symbol = defaultProperty;
            }
        }
        // 如果 symbol 的类型是 Categorical， 则加上 CheckFlag.numberIndex
        if (symbol.type === basicTypes.categorical) {
            symbol.set("checkFlag", symbol.checkFlag | t.CheckFlag.numberIndex);
        }
        // 忽略any类型的索引错误。
        if (symbol.type === basicTypes.any) {
            for (const index of indices) {
                this.computeExpression(index, scope);
            }
        }
        // 如果符号是元数据，检查元数据类型
        else if (symbol.is(t.SymbolKind.metadata)) {
            this._context.indexExpressionContext = symbol;
            const index = indices[indices.length - 1];
            const indexSymbol = this.computeExpression(index, scope);
            // 如果维度超过1，后续维度抛出错误
            if (indices.length > 1) {
                for (let i = 0; i < indices.length - 1; i++) {
                    const expr = indices[i];
                    this.computeExpression(expr, scope);
                    this.error(errorMessages.indexRankError, expr, symbol.name);
                }
            }
            const field = symbol.declaration;
            if (field.isMDMField()) {
                // 数字索引
                if (field.fieldFlag === t.MDMFieldFlags.numericLoop) {
                    let compare = basicTypes.long.compare(indexSymbol.type);
                    if (!compare.success) {
                        this.error(errorMessages.invalidIndexType, index, getTypeFullName(indexSymbol.type));
                    }
                }
                // Categorical 索引， 允许 字符串值或分类值
                else if (field.fieldFlag === t.MDMFieldFlags.categoricalLoop) {
                    let compare = basicTypes.string.compare(indexSymbol.type);
                    if (!compare.success) {
                        compare = basicTypes.categorical.compare(indexSymbol.type);
                    }
                    if (!compare.success) {
                        this.error(errorMessages.invalidIndexType, index, getTypeFullName(indexSymbol.type));
                    }
                }
                else {
                    this.error(errorMessages.invalidIndexType, index, getTypeFullName(indexSymbol.type));
                }
            }
        }
        // 如果找到的符号没有登记索引或访问索引维度错误，抛出错误。
        else if (!symbol.isCheckFlag(t.CheckFlag.indexable)) {
            this.error(errorMessages.indexRankError, finalName, finalName.text);
        }
        else {
            // 检查索引类型
            let hasIterator = false;
            for (let i = indices.length - 1; i >= 0; i--) {
                const index = indices[i];
                const indexSymbol = this.computeExpression(index, scope);
                const indexType = indexSymbol.type;
                // object iterator
                if (index.isExpression<t.ObjectCollectionIterator>(t.SyntaxKind.objectCollectionIterator)) {
                    if (hasIterator || !(symbol.is(t.SymbolKind.metadata) && symbol.isMDM(t.SymbolMDMKind.field))) {
                        this.error(errorMessages.invalidObjectIterator, index);
                    }
                    if (this._state.isAssignmentExpressionRight ||
                        this._state.isBinaryExpressionRight ||
                        this._state.isBinaryExpressionLeft
                    ) {
                        this.error(errorMessages.objectCollectionIteratorCannotPlaceRightHand, index);
                    }
                    hasIterator = true;
                    continue;
                }
                // categorical
                if (index.isCategoricalLiteral()) {
                    // 检查符号是否是MetadataField
                    if (!(symbol.is(t.SymbolKind.metadata) && symbol.isMDM(t.SymbolMDMKind.field))) {
                        this.error(errorMessages.invalidIndexType, index, "分类值");
                    }
                    else if (index.categories.length !== 1) {
                        this.error(errorMessages.categoricalIndexLengthDoNotEqualOne, index);
                    }
                    else if (symbol.locals.size > 0 && index.categories[0].lower && !symbol.locals.get(index.categories[0].lower.text)) {
                        this.error(warningMessages.canNotFindCategoricalInField, index, symbol.name, index.categories[0].lower.text);
                    }
                    continue;
                }
                let fit = (symbol.isCheckFlag(t.CheckFlag.numberIndex) && basicTypes.long.isCompatible(indexType))
                    || (symbol.isCheckFlag(t.CheckFlag.stringIndex) && basicTypes.string.isCompatible(indexType));
                if (!fit) {
                    this.error(errorMessages.invalidIndexType, index, indexType.name);
                }
            }
        }
        let finalSymbol: t.Symbol;
        // 检查对象是否是MetadataField，如果是，返回结果对象
        if (symbol.is(t.SymbolKind.metadata)) {
            finalSymbol = symbol;
            if (this._context.callExpression) {
                this._context.callExpressionContext = symbol;
            }
        }
        else {
            // 创建最终返回的符号，此符号与索引对象的符号有所不同，应当另外创建，只需要将原始符号的维度移除即可。
            finalSymbol = createSymbolFromNode(
                node,
                ++this._symbolCount,
                this._converter,
                t.SymbolKind.variable,
                undefined,
                symbol.type.original ?? symbol.type);
        }
        // 为了方便`MemberExpression`检索符号，将符号添加到`IndexExpression`上，但是不应该将`IndexExpression`更新到符号的version中。
        (node as Mutable<t.Node>).symbol = finalSymbol;
        this._context.indexExpression = inIndexExpression;
        return finalSymbol;
    }

    private computeMemberExpression(node: t.MemberExpr, scope: t.ReadonlySymbolTableLike): t.Symbol {
        const object = node.object;
        let objectSymbol: t.Symbol | undefined;
        // object符号已有
        if (object.symbol !== undefined) {
            objectSymbol = object.symbol;
        }
        // object.member.member
        else if (object.isExpression<t.MemberExpr>(t.SyntaxKind.memberExpr)) {
            objectSymbol = this.computeMemberExpression(object, scope);
        }
        // object().member
        else if (object.isExpression<t.CallExpr>(t.SyntaxKind.callExpr)) {
            objectSymbol = this.computeCallExpression(object, scope);
        }
        // object[index].member
        else if (object.isExpression<t.IndexExpr>(t.SyntaxKind.indexExpr)) {
            objectSymbol = this.computeIndexExpression(object, scope);
        }
        // identifier.member
        else {
            objectSymbol = this.computeIdentifier(object, scope, false);
        }
        let members: t.ReadonlySymbolMap | undefined;
        let memberSymbol: t.Symbol | undefined;
        let latest = objectSymbol.type;
        // 首先检查object符号是否是metadata field
        // 对于Metadata Field，首先判断是否是下级Field，之后再检查是否是MDMField类型属性
        if (objectSymbol.isMDM(t.SymbolMDMKind.field)) {
            memberSymbol = objectSymbol.members.get(node.member.text.toLowerCase());
            this._context.expressionContext = memberSymbol;
            if (this._context.callExpression) {
                this._context.callExpressionContext = memberSymbol;
            }
        }
        if (!memberSymbol && latest && latest.isType() && (members = latest.symbol.members)) {
            memberSymbol = members.get(node.member.text.toLowerCase()) ?? objectSymbol.getMaybeDefaultProperty(s => stringEqual(s.escapedName, node.member.text));
        }
        // 如果未找到成员，并且父对象是CallExpression，尝试查找函数
        if (!memberSymbol && node.parent?.isExpression<t.CallExpr>(t.SyntaxKind.callExpr)) {
            let calleeSymbol = this.getSymbol(node.member.text, scope);
            if (calleeSymbol && calleeSymbol.is(t.SymbolKind.function)) {
                memberSymbol = calleeSymbol;
            }
        }
        // 如果最终还是没有找到，创建未定义符号
        if (!memberSymbol) {
            memberSymbol = createSymbolFromIdentifier(
                node.member,
                ++this._symbolCount,
                this._converter,
                t.SymbolKind.undefined | t.SymbolKind.variable,
                undefined,
                basicTypes.any
            );
        }
        updateSymbolNode(memberSymbol, node.member);
        return memberSymbol;
    }

    private computeCallExpression(node: t.CallExpr, scope: t.ReadonlySymbolTableLike): t.Symbol {
        // 保存 context
        const savedCallExpression = this._context.callExpression;
        // 设置 context
        this._context.callExpression = true;
        this._context.callExpressionCallee = true;
        let calleeSymbol = this.computeIdentifierOrHeigher(node.callee, scope);
        this._context.callExpressionCallee = false;
        let functionName = node.callee.isExpression<t.MemberExpr>(t.SyntaxKind.memberExpr) ? node.callee.member : node.callee;
        this._context.callExpressionFunctionName = functionName.text.toLowerCase();
        // 尝试获取通过'.'调用的父级对象的元数据符号
        if (node.callByDot && node.callee.isExpression<t.MemberExpr>(t.SyntaxKind.memberExpr)) {
            let calleeSymbol = node.callee.object.symbol;
            if (calleeSymbol && calleeSymbol.isMDM(t.SymbolMDMKind.field) && contexSupportedFunctions.has(functionName.text.toLowerCase())) {
                this._context.callExpressionContext = calleeSymbol;
            }
        }
        const expressionSymbol = createSymbolFromNode(node, ++this._symbolCount, this._converter);
        // 忽略 any 类型产生的错误
        if (!(calleeSymbol.is(t.SymbolKind.variable) && calleeSymbol.type.is(t.TypeFlag.any)) &&
            !(calleeSymbol.name !== "defined" && this._state.isPreprocessorIfStatementCondition)) {
            // 找到的符号不是可调用的类型
            if (!calleeSymbol.is(t.SymbolKind.functionLike)) {
                this.error(errorMessages.symbolIsNotAFunction, functionName, functionName.text);
            }
            // 检查在#if中时，是否时参数宏
            else if (this._state.isPreprocessorIfStatementCondition && !calleeSymbol.is(t.SymbolKind.macroFunction)) {
                this.error(errorMessages.macroIsNotDeclared, functionName, functionName.text);
            }
            // CreateObject
            else if (node.callee.isIdentifier() && !calleeSymbol.parent && calleeSymbol.escapedName === "createobject") {
                // 缺少参数
                if (node.arguments.length !== 1) {
                    this.error(errorMessages.expectNumberOfArguments,
                        node.callee, "CreateObject", "1", node.arguments.length.toString());
                }
                else if (!node.arguments[0].isStringLiteral()) {
                    const argSymbol = this.computeExpression(node.arguments[0], scope);
                    if (!argSymbol.type.isCompatible(basicTypes.string)) {
                        this.error(errorMessages.cannotAssignBetweenDifferentType,
                            node.arguments[0], getTypeFullName(argSymbol.type), "string");
                    }
                } else {
                    const arg = node.arguments[0];
                    let find = this.getSymbol(arg.text.split("."), scope);
                    if (find) {
                        return find;
                    }
                    else {
                        this.error(errorMessages.typeIsNotExist, arg, arg.text);
                    }
                }
            }
            // 可调用类型
            // 函数声明允许不定长参数：`ParamArray parameter[] As Type`
            // 所以在匹配参数数量时，不能简单比较参数数量
            else {
                let functionCallByDot = calleeSymbol.is(t.SymbolKind.function) && node.callByDot;
                // 检查通过'.'调用函数时，第一个参数的匹配情况
                if ((calleeSymbol.kind & t.SymbolKind.function) > 1 &&
                    node.callByDot &&
                    node.callee.isExpression<t.MemberExpr>(t.SyntaxKind.memberExpr)) {
                    if (calleeSymbol.parameters.length === 0) {
                        this.error(errorMessages.expectNumberOfArguments,
                            node,
                            calleeSymbol.name,
                            "0",
                            (node.arguments.length + 1).toString());
                    }
                    else {
                        let objectSymbol = node.callee.isExpression<t.MemberExpr>(t.SyntaxKind.memberExpr) ? node.callee.object.symbol : undefined;
                        if (objectSymbol) {
                            let firstCompare = calleeSymbol
                                .parameters[0]
                                .getDefaultProperty()
                                .type
                                .compare(objectSymbol.getDefaultProperty().type, true);
                            if (!firstCompare.success) {
                                this.error(errorMessages.cannotAssignBetweenDifferentType,
                                    node.callee.object,
                                    getTypeFullName(objectSymbol.type),
                                    getTypeFullName(calleeSymbol.parameters[0].type));
                            }
                        }
                    }
                }

                let checkParameter: t.Symbol | undefined;
                let savedMacroFunctionState = this._context.macroFunctionParameter;
                this._context.macroFunctionParameter = calleeSymbol.is(t.SymbolKind.macroFunction);
                for (let i = 0; i < node.arguments.length; i++) {
                    const argNode = node.arguments[i];
                    const argSymbol = this.computeExpression(argNode, scope);

                    let argIndex = functionCallByDot ? i + 1 : i;
                    if (argIndex < calleeSymbol.parameters.length) {
                        checkParameter = calleeSymbol.parameters[argIndex];
                    }
                    else {
                        if (!(checkParameter && (checkParameter.checkFlag & t.CheckFlag.paramArray) > 1)) {
                            checkParameter = undefined;
                        }
                    }

                    if (checkParameter) {
                        // 可能是跳过可选参数
                        if (argNode.isMissingNode()) {
                            if ((checkParameter.checkFlag & t.CheckFlag.optional) === 0) {
                                this.error(errorMessages.parameterIsNotOptional, argNode, checkParameter.name);
                            }
                            continue;
                        }

                        let compareResult = argSymbol
                            .getDefaultProperty()
                            .type
                            .compare(checkParameter.getDefaultProperty().type, true);
                        if (!compareResult.success) {
                            this.error(
                                errorMessages.cannotAssignBetweenDifferentType,
                                node.arguments[i],
                                getTypeFullName(checkParameter.type),
                                getTypeFullName(argSymbol.type)
                            );
                        }
                    }
                }
                let definedParamNumber = calleeSymbol.parameters.length;
                let acceptParamNumber = node.arguments.length + (functionCallByDot ? 1 : 0);
                // 如果`checkParameter = undefined`，表示接收的参数比定义给的多
                if (!checkParameter && acceptParamNumber > definedParamNumber) {
                    let start = node.arguments.start;
                    let end = node.arguments.end;
                    this.error(
                        errorMessages.expectNumberOfArguments,
                        createTextRange(start, end),
                        calleeSymbol.name,
                        definedParamNumber.toString(),
                        acceptParamNumber.toString()
                    );
                }
                // 接收的参数比定义的少
                if (acceptParamNumber < definedParamNumber) {
                    const missingArg: t.Symbol[] = [];
                    // `ParamArray`修饰的参数默认为可选参数
                    for (let i = acceptParamNumber; i < definedParamNumber; i++) {
                        const param = calleeSymbol.parameters[i];
                        // 不是可选的参数，计入缺失参数
                        if ((param.checkFlag & t.CheckFlag.optionalLike) === 0) {
                            missingArg.push(param);
                        }
                    }
                    if (missingArg.length > 0) {
                        this.error(errorMessages.parameterIsNotOptional, node, missingArg.map(param => param.name).join(","));
                    }
                }
                this._context.macroFunctionParameter = savedMacroFunctionState;
            }
            updateSymbolNode(expressionSymbol, node, calleeSymbol.type);
        }
        // defined
        else if (this._state.isPreprocessorIfStatementCondition &&
            node.callee.isIdentifier() &&
            node.callee.text === "defined") {
            // 检查参数数量
            if (node.arguments.length !== 1) {
                this.error(errorMessages.expectNumberOfArguments, node, "defined", "1", node.arguments.length.toString());
            }
            else {
                const arg = node.arguments[0];
                if (arg.isIdentifier()) {
                    expressionSymbol.tracker.setNodeType(arg, basicTypes.boolean);
                }
                else {
                    this.error(errorMessages.unexpectedToken, arg, "标识符");
                }
            }
        }
        else {
            for (const exp of node.arguments) {
                this.computeExpression(exp, scope);
            }
        }
        this._context.callExpression = savedCallExpression;
        return expressionSymbol;
    }

    private computeIdentifierOrHeigher(expression: t.Identifier | t.MemberExpr | t.IndexExpr | t.CallExpr, scope: t.ReadonlySymbolTableLike): t.Symbol {
        if (expression.isIdentifier()) {
            if (this._context.callExpressionCallee) {
                this._context.callExpressionCalleeName = true;
            }
            const symbol = this.computeIdentifier(expression, scope, false);
            if (this._context.callExpressionCallee) {
                this._context.callExpressionCalleeName = false;
            }
            return symbol;
        }
        if (expression.isExpression<t.MemberExpr>(t.SyntaxKind.memberExpr)) {
            return this.computeMemberExpression(expression, scope);
        }
        if (expression.isExpression<t.CallExpr>(t.SyntaxKind.callExpr)) {
            return this.computeCallExpression(expression, scope);
        }
        return this.computeIndexExpression(expression, scope);
    }

    private computeUnaryExpression(expression: t.UnaryExpr, scope: t.ReadonlySymbolTableLike): t.Symbol {
        let exprSymbol = this.computeExpression(expression.expression, scope);
        if (exprSymbol.type.flag & t.TypeFlag.void) {
            this.error(errorMessages.unaryExpressionNeedReturnValue, expression.expression);
        }
        return createSymbolFromNode(
            expression,
            ++this._symbolCount,
            this._converter,
            undefined,
            undefined,
            basicTypes.boolean);
    }

    private computeAssignmentExpression(expression: t.AssignmentExpression, scope: t.ReadonlySymbolTableLike) {
        // context
        this.clearContext();
        // 左值
        this._state.isAssignmentExpressionLeft = true;
        let left = this.computeExpression(expression.left, scope);
        this._state.isAssignmentExpressionLeft = false;
        // 添加操作符
        this._context.operator = expression.operator;
        // 右值
        this._state.isAssignmentExpressionRight = true;
        let right = this.computeExpression(expression.right, scope);
        this._state.isAssignmentExpressionRight = false;
        // 如果右值不是BinaryExpression，单独检查上下文
        if (!expression.right.isExpression<t.BinaryExpression>(t.SyntaxKind.binaryExpr) && right.isMDM(t.SymbolMDMKind.field)) {
            this.checkBinaryExpressionLeftRightMetadataField(expression.right, right);
        }
        // 移除上下文对象
       this.clearContext();

        let finalSymbol: t.Symbol;
        // 检查左值是否是只读的
        if (!expression.left.isMissingNode() && left.isCheckFlag(t.CheckFlag.readonly)) {
            // 尝试读取默认属性
            left = left.getDefaultProperty();
            if (left.isCheckFlag(t.CheckFlag.readonly)) {
                this.error(errorMessages.assignmentLeftIsReadonly, expression.left, left.name);
            }
            finalSymbol = left;
        }
        // 检查右值是否是只写的
        else if (!expression.right.isMissingNode() && right.isCheckFlag(t.CheckFlag.writeonly)) {
            this.error(errorMessages.assignmentRightIsWriteonly, expression.right, right.name);
            finalSymbol = left;
        }
        else {
            // 检查右值类型是否可以赋值给左值
            let compare = left.type.compare(right.type, true, false);
            // 如果左值是`Identifier`并且是本地变量，需要更新类型
            if (expression.left.isIdentifier() && left.is(t.SymbolKind.variable) && !left.is(t.SymbolKind.undefined)) {
                updateSymbolNode(left, expression.left, compare.type);
                finalSymbol = left;
            }
            // 如果左值是`IndexExpression`且是本地变量，需要更新类型
            else if (expression.left.isExpression<t.IndexExpr>(t.SyntaxKind.indexExpr) && left.is(t.SymbolKind.variable)) {
                let leftNode = expression.left;
                if (leftNode.object.isIdentifier()) {
                    updateSymbolNode(leftNode.object.symbol, leftNode.object, compare.type);
                }
                finalSymbol = left;
            }
            // 如果失败，检查左值和右值是否有默认属性
            else if (!compare.success) {
                let leftDefaultProperty = left.getDefaultProperty();
                let rightDefaultProperty = right.getDefaultProperty();
                compare = leftDefaultProperty.type.compare(rightDefaultProperty.type, true, false);
                // 对比成功时，最终符号是右值符号
                if (compare.success) {
                    finalSymbol = right;
                    // 如果左值是`IndexExpression`并且是本地数组，需要更新类型
                    if (expression.left.isExpression<t.IndexExpr>(t.SyntaxKind.indexExpr) &&
                        expression.left.object.isIdentifier()) {
                        // 数组定义符号在`left.object`对象上
                        const objSymbol = expression.left.object.symbol;
                        if (objSymbol &&
                            objSymbol.is(t.SymbolKind.array) &&
                            objSymbol.declaration.isDeclaration<t.ArrayDecl>(t.SyntaxKind.arrayDecl)) {
                            objSymbol.tracker.setNodeType(expression.left.object,
                                compare.type.rankType(objSymbol.type.rank));
                        }
                    }
                }
                else {
                    finalSymbol = left;
                    this.error(errorMessages.cannotAssignBetweenDifferentType,
                        expression,
                        getTypeFullName(right.type),
                        getTypeFullName(left.type));
                }
            }
            else {
                finalSymbol = right;
            }
        }
        return finalSymbol;
    }

    private checkBinaryExpressionLeftRightMetadataField(exp: t.Expression, symbol: t.Symbol) {
        // 只有 '+' 或 '=' 号才进行检查，其他的忽略
        if (!this._context.operator || (this._context.operator.kind !== t.SyntaxKind.plus && this._context.operator.kind !== t.SyntaxKind.equal)) {
            return;
        }
        if (symbol.isMDM(t.SymbolMDMKind.field)) {
            if (this._context.expressionContext && !this.checkMetadataFieldCategoriesCoincidence(symbol)) {
                this.error(
                    warningMessages.fieldCategoriesIsNotFullyCoincident,
                    exp,
                    getMetadataFieldFullName(symbol),
                    getMetadataFieldFullName(this._context.expressionContext)
                );
            }
            else if (!this._context.expressionContext) {
                this._context.expressionContext = symbol;
            }
        }
    }

    private computeBinaryExpression(expression: t.BinaryExpression, scope: t.ReadonlySymbolTableLike) {
        const savedContext = this.saveContext();
        // left
        this._state.isBinaryExpressionLeft = true;
        let left = this.computeExpression(expression.left, scope);
        this._state.isBinaryExpressionLeft = false;
        // 检查左值符号是否是元数据符号
        this.checkBinaryExpressionLeftRightMetadataField(expression.left, left);
        // 检查操作符
        this._context.operator = expression.operator;
        // right
        this._state.isBinaryExpressionRight = true;
        let right = this.computeExpression(expression.right, scope);
        this._state.isBinaryExpressionRight = false;
        // 检查右值符号是否是元数据符号
        this.checkBinaryExpressionLeftRightMetadataField(expression.right, right);
        // context
        this.recoveryContext(savedContext);
        // compare
        let compare = left.type.compare(right.type, true, false);
        if (!compare.success) {
            // 如果第一次失败，尝试对比左右值的默认属性
            left = left.getDefaultProperty();
            right = right.getDefaultProperty();
            compare = left.type.compare(right.type, true, false);
            if (!compare.success) {
                this.error(errorMessages.cannotAssignBetweenDifferentType, expression,
                    getTypeFullName(left.type), getTypeFullName(right.type));
            }
        }
        return createSymbolFromNode(
            expression,
            ++this._symbolCount,
            this._converter,
            undefined,
            undefined,
            compare.type
        );
    }

    private computeExpression(expression: t.Expression, scope: t.ReadonlySymbolTableLike): t.Symbol {
        if (!expression.isMissingNode()) {
            switch (expression.kind) {
                case t.SyntaxKind.stringLiteral:
                case t.SyntaxKind.numberLiteral:
                case t.SyntaxKind.trueLiteral:
                case t.SyntaxKind.falseLiteral:
                case t.SyntaxKind.nullLiteral:
                case t.SyntaxKind.categoricalItemLiteral:
                case t.SyntaxKind.categoricalLiteral:
                    if (expression.kind === t.SyntaxKind.categoricalLiteral) {
                        let existNamesSet = new Set<string>();
                        for (const item of (expression as t.CategoricalLiteral).categories) {
                            this.computeCategoricalItemLiteral(item, existNamesSet);
                        }
                    }
                    return createSymbolFromLiteral(expression as t.Literal, this._converter, ++this._symbolCount);
                case t.SyntaxKind.identifier:
                    return this.computeIdentifier(expression as t.Identifier, scope, false);
                case t.SyntaxKind.memberExpr:
                    return this.computeMemberExpression(expression as t.MemberExpr, scope);
                case t.SyntaxKind.indexExpr:
                    return this.computeIndexExpression(expression as t.IndexExpr, scope);
                case t.SyntaxKind.callExpr:
                    return this.computeCallExpression(expression as t.CallExpr, scope);
                case t.SyntaxKind.unaryExpr:
                    return this.computeUnaryExpression(expression as t.UnaryExpr, scope);
                case t.SyntaxKind.binaryExpr:
                    return expression.isAssignmentExpression() ?
                        this.computeAssignmentExpression(expression as t.AssignmentExpression, scope) :
                        this.computeBinaryExpression(expression as t.BinaryExpression, scope);
            }
        }
        return createSymbolFromNode(expression, ++this._symbolCount, this._converter);
    }

    private computeBindingType(node: t.Binding, scope?: t.ReadonlySymbolTableLike, module?: t.ModuleSignature): t.LocalType {
        let binding = node.type;
        let normalType = isNodeArray(binding);

        if ((normalType && (binding as t.NodeArray<t.Identifier>).length === 0) ||
            (!normalType && ((binding as t.ArrayTypeSignature).isMissingNode() || (binding as t.ArrayTypeSignature).fullName.length === 0))) {
            return basicTypes.any;
        }

        let fullNames = normalType ?
            (node.type as t.NodeArray<t.Identifier>) :
            (node.type as t.ArrayTypeSignature).fullName;

        if (fullNames.length === 1) {
            // 首先判断是否是基础类型
            let basicType = textToBasicTypes(fullNames[0].text);
            if (basicType) {
                // Type[]
                if (!normalType) {
                    return basicType.rankType((binding as t.ArrayTypeSignature).rank);
                }
                return basicType;
            }
        }

        let symbol = this.getSymbolFromModule(fullNames, module, scope, true);
        if (symbol) {
            if (!normalType) {
                return symbol.type.rankType((binding as t.ArrayTypeSignature).rank);
            }
            return symbol.type;
        }

        // 未找到，挂起
        this.delay(scope => {
            let find = this.getSymbolFromModule(fullNames, module, scope, false);
            if (find) {
                let parentSymbol = node.parent.name.symbol!;
                let type = normalType ?
                    find.type :
                    find.type.rankType((binding as t.ArrayTypeSignature).rank);
                // 更新父对象声明符号类型
                updateSymbolNode(parentSymbol, node.parent.name, type);
            }
        });
        return basicTypes.any;
    }

    private checkRedeclare(node: t.Declaration | t.Signature<t.SignatureKind>, map: t.ReadonlySymbolTableLike, diagnostic?: DiagnosticMessage): t.Symbol | undefined {
        if (node.isMissingNode()) {
            return;
        }
        let name = node.name.text;
        // 查找本地声明变量
        let result = this.getSymbol(name, map, t.SymbolScope.local) || this.getSymbol(name, map, t.SymbolScope.macro);
        if (result) {
            if (result.is(t.SymbolKind.macro)) {
                this.error(warningMessages.localVariableNameConflictWithMacro, node.name, name);
            }
            else if (diagnostic) {
                this.error(diagnostic, node.name, name);
            }
        }
        return result;
    }

    private startLocationBlock(node: t.LocationBlockNode) {
        let current = node.locations.symbols;
        this._programLocations.push(current);
        for (const loc of node.locations.nodes) {
            this.declareLineLocation(loc);
        }
    }

    private endLocationBlock() {
        if (this._programLocations.length > 0) {
            this._programLocations.pop();
        }
    }

    private declareLineLocation(location: t.LineLocation) {
        let symbolTable: Map<string, t.Symbol>;
        if (this._programLocations.length === 0) {
            symbolTable = new Map();
            this._programLocations.push(symbolTable);
        }
        else {
            symbolTable = this._programLocations[this._programLocations.length - 1];
        }
        let exist = symbolTable.get(location.name.text.toLowerCase());
        if (exist) {
            this.error(errorMessages.locationRedeclaration, location.name, location.name.text);
        }
        else {
            const symbol = this.createSymbol(
                t.SymbolKind.location,
                location.name.text,
                t.CheckFlag.readonly,
                location
            );
            updateSymbolNode(symbol, location.name);
            symbolTable.set(symbol.escapedName, symbol);
        }
    }

    private declareLocalVariable(declaration: t.VariableDecl, scope: t.ReadonlySymbolTableLike) {
        let symbol = this.checkRedeclare(declaration, scope, errorMessages.variableReDeclaration);
        if (symbol) {
            return symbol;
        }
        symbol = this.createSymbol(
            t.SymbolKind.variable,
            declaration.name.text,
            t.CheckFlag.none,
            declaration,
            basicTypes.any);
        this.declareSymbolToSymbolTableLike(symbol,
            scope,
            t.SymbolScope.local,
            errorMessages.variableReDeclaration);
        updateSymbolNode(symbol, declaration.name);
        // 更新全局符号表
        this.declareToGlobalVariables(symbol);
        return symbol;
    }

    private declareLocalArray(declaration: t.ArrayDecl, scope: t.ReadonlySymbolTableLike) {
        let symbol = this.checkRedeclare(declaration, scope, errorMessages.variableReDeclaration);
        if (symbol) {
            return symbol;
        }
        symbol = this.createSymbol(t.SymbolKind.array,
            declaration.name.text,
            t.CheckFlag.numberIndex,
            declaration,
            basicTypes.any.rankType(declaration.rank));
        this.declareSymbolToSymbolTableLike(symbol,
            scope,
            t.SymbolScope.local,
            errorMessages.variableReDeclaration);
        updateSymbolNode(symbol, declaration.name);
        // 更新全局符号表
        this.declareToGlobalVariables(symbol);
        return symbol;
    }

    private declareLocalDeclarations(node: t.LocalDecls, scope: t.ReadonlySymbolTableLike) {
        for (const declaration of node.declarations) {
            if (declaration.isDeclaration<t.VariableDecl>(t.SyntaxKind.varDecl)) {
                this.declareLocalVariable(declaration, scope);
            }
            else {
                this.declareLocalArray(declaration, scope);
            }
        }
    }

    private declareConstant(node: t.ConstantDeclaration, arg: t.ReadonlySymbolTableLike, parentSymbol?: t.Symbol) {
        let symbol = this.checkRedeclare(node, arg, errorMessages.variableReDeclaration);
        if (symbol) {
            return;
        }
        // 如果声明节点有错误，分两种情况：
        // + `name`不是missingNode，正常声明，不提供初始值，此时错误Parser已经抛出
        // + `name`是missingNode，无法声明，直接返回
        if (node.name.isMissingNode()) {
            return;
        }

        let type: t.IntrinsicType | t.Type = basicTypes.any;
        let initalization: number | string | boolean | undefined;

        if (!node.initialize.isMissingNode()) {
            let right = node.initialize.right;
            if (right.isStringLiteral()) {
                type = basicTypes.string;
                initalization = `"${right.text}"`;
            }
            else if (right.isCategoricalLiteral()) {
                type = basicTypes.categorical;
                initalization = right.text;
            }
            else if (right.isNumberLiteral()) {
                type = right.isFloat ? basicTypes.double : basicTypes.long;
                initalization = right.value;
            }
            else if (right.isTrueLiteral()) {
                type = basicTypes.boolean;
                initalization = true;
            }
            else if (right.isFalseLiteral()) {
                type = basicTypes.boolean;
                initalization = false;
            }
            else {
                type = basicTypes.any;
                initalization = right.text;
            }
        }

        symbol = this.createSymbol(
            t.SymbolKind.constant,
            node.name.text,
            t.CheckFlag.readonly,
            node,
            type
        );
        symbol.set("initialization", initalization);
        symbol.set("parent", parentSymbol);
        this.declareSymbolToSymbolTableLike(
            symbol,
            arg,
            t.SymbolScope.local,
            errorMessages.variableReDeclaration
        );
        updateSymbolNode(symbol, node.name);
        // 更新全局符号表
        this.declareToGlobalVariables(symbol);
        return symbol;
    }

    // Macro
    private declareMacro(node: t.PPDefine, scope: t.ReadonlySymbolTableLike) {
        // `name` 是missingNode时，无法声明，直接返回
        if (node.name.isMissingNode()) {
            return;
        }

        const symbol = this.createSymbol(
            node.parameters !== undefined && node.parameters.length > 0 ? t.SymbolKind.macroFunction : t.SymbolKind.macroVariable,
            node.name.text,
            t.CheckFlag.readonly,
            node);
        if (node.parameters && node.parameters.length > 0) {
            let paramTable: t.Symbol[] = [];
            node.parameters.forEach(p => {
                const paramSymbol = this.createSymbol(t.SymbolKind.parameter, p.text, t.CheckFlag.none, this._converter.convertIdentifierToVariableDeclaration(p.name));
                paramSymbol.set("parent", symbol);
                if (paramTable.find(e => stringEqual(e.name, p.text))) {
                    this.error(errorMessages.parameterRedeclaration, p, p.text);
                }
                paramTable.push(paramSymbol);
            });
            symbol.parameters.push(...paramTable);
        }
        // 宏定义允许重新定义，覆盖已有定义
        // 宏名区分大小写
        this.declareSymbolToSymbolTableLike(symbol,
            scope,
            t.SymbolScope.macro);
        updateSymbolNode(symbol, node.name);
    }

    private declareParameters(parentSymbol: t.Symbol, parameters: readonly t.ParameterSignature[], table: t.ReadonlySymbolTableLike, module?: t.ModuleSignature): t.Symbol[] {
        const symbols: t.Symbol[] = [];
        let hasOptional = false;
        for (let i = 0; i < parameters.length; i++) {
            const p = parameters[i];
            let declaration = p.declaration;
            if (!declaration) {
                continue;
            }

            if (symbols.find(e => stringEqual(e.name, p.name.text))) {
                this.error(errorMessages.parameterRedeclaration, declaration.name, declaration.name.text);
                continue;
            }

            let parameterSymbol: t.Symbol;
            let checkFlag = getParameterCheckFlag(p);
            //// `ParamArray`参数必须位于最后
            //if (p.paramArray && i < parameters.length - 1) {
            //    this.error(errorMessages.paramArrayParameterMustAtLast, p.paramArray);
            //    checkFlag &= (~CheckFlag.paramArray);
            //    checkFlag |= CheckFlag.optional;
            //}

            if (!hasOptional && (checkFlag & t.CheckFlag.optionalLike) > 0) {
                hasOptional = true;
            }
            // 非`Optional`参数不能位于`Optional`参数后面
            else if (hasOptional && (checkFlag & t.CheckFlag.optionalLike) === 0) {
                checkFlag |= t.CheckFlag.optional;
                this.error(errorMessages.parameterCannotPlaceBehindOptionalParameter, p.name);
            }

            parameterSymbol = this.createSymbol(declaration.isDeclaration<t.VariableDecl>(t.SyntaxKind.varDecl) ? t.SymbolKind.parameter : (t.SymbolKind.array | t.SymbolKind.parameter), declaration.name.text, checkFlag, declaration);
            symbols.push(parameterSymbol);
            parameterSymbol.set("parent", parentSymbol);
            if (declaration.bindingType) {
                parameterSymbol.tracker.setNodeType(declaration.name, this.computeBindingType(declaration.bindingType, undefined, module));
            }
            updateSymbolNode(parameterSymbol, p.name);
            if (isSymbolTable(table)) {
                (table as t.SymbolTable).set(parameterSymbol);
            }
            else {
                (table as Map<string, t.Symbol>).set(parameterSymbol.escapedName, parameterSymbol);
            }
        }
        return symbols;
    }

    private declareLocalFunction(node: t.FunctionDeclaration, scope: t.ReadonlySymbolTableLike) {
        let symbol = this.checkRedeclare(node, scope, errorMessages.functionRedeclaration);
        if (symbol) {
            return symbol;
        }
        symbol = this.createSymbol(
            t.SymbolKind.function,
            node.name.text,
            t.CheckFlag.readonly,
            node,
            node.hasReturn ? basicTypes.any : basicTypes.void
        );
        // 声明参数，此处应当使用函数自身的定义域
        symbol.parameters.push(...this.declareParameters(symbol, node.parameters, symbol.locals as Map<string, t.Symbol>));
        for (const param of symbol.parameters) {
            node.locals.set(param, t.SymbolScope.local);
        }
        updateSymbolNode(symbol, node.name);
        this.declareSymbolToSymbolTableLike(symbol, scope, t.SymbolScope.function, errorMessages.functionRedeclaration);
        return symbol;
    }

    private declarePropertySignature(signature: t.PropertySignature, table: t.ReadonlySymbolMap, parentSymbol: t.Symbol): t.Symbol {
        const symbol = this.createSymbol(t.SymbolKind.property, signature.name.text, getModifierCheckFlag(signature), signature);
        symbol.set("parent", parentSymbol);
        const module = getModuleSignature(parentSymbol);
        if (signature.parameters) {
            symbol.parameters.push(...this.declareParameters(symbol, signature.parameters, symbol.locals as Map<string, t.Symbol>, module));
            // 如果属性有参数，将符号标记为可索引
            if (symbol.parameters.length > 0) {
                let checkFlag = symbol.checkFlag;
                let firstParam = symbol.parameters[0];
                if (firstParam.type.flag & t.TypeFlag.long) {
                    checkFlag |= t.CheckFlag.numberIndex;
                }
                else {
                    checkFlag |= (t.CheckFlag.stringIndex | t.CheckFlag.numberIndex);
                }
                symbol.set("checkFlag", checkFlag);
            }
        }
        updateSymbolNode(symbol, signature.name, this.computeBindingType(signature.bindingType, undefined, module));
        this.declareSymbolToSymbolTableLike(symbol, table, t.SymbolScope.declare, errorMessages.propertyRedeclaration);
        return symbol;
    }

    private declareMethodSignature(signature: t.FunctionSignature, table: t.ReadonlySymbolMap, parentSymbol: t.Symbol): t.Symbol {
        const module = getModuleSignature(parentSymbol);
        const symbol = this.createSymbol(
            t.SymbolKind.method,
            signature.name.text,
            t.CheckFlag.readonly,
            signature,
            signature.bindingType ?
                this.computeBindingType(signature.bindingType, undefined, module) :
                basicTypes.void);
        symbol.set("parent", parentSymbol);
        updateSymbolNode(symbol, signature.name);
        if (signature.parameters) {
            symbol.parameters.push(...this.declareParameters(symbol, signature.parameters, symbol.locals as Map<string, t.Symbol>, module));
        }
        this.declareSymbolToSymbolTableLike(symbol, table, t.SymbolScope.declare, parentSymbol.is(t.SymbolKind.class) ? errorMessages.classMemberRedelcaration : errorMessages.interfaceMemberRedeclaration);
        return symbol;
    }

    private declareFunctionSignature(signature: t.FunctionSignature, table: t.ReadonlySymbolMap): t.Symbol {
        const symbol = this.createSymbol(
            t.SymbolKind.function,
            signature.name.text,
            t.CheckFlag.readonly,
            signature,
            signature.bindingType ?
                this.computeBindingType(signature.bindingType) :
                basicTypes.void);
        updateSymbolNode(symbol, signature.name);
        if (signature.parameters) {
            symbol.parameters.push(...this.declareParameters(symbol, signature.parameters, symbol.locals));
        }
        this.declareSymbolToSymbolTableLike(symbol, table, t.SymbolScope.declare, errorMessages.classMemberRedelcaration);
        return symbol;
    }

    private updateClassOrInterfaceInheritsOrImplements(signature: t.InterfaceSignature | t.ClassSignature, signatureSymbol: t.Symbol, inheritOrImplementSymbol: t.Symbol, inheritOrImplementRange: t.ReadonlyTextRange, inherits: boolean): void {
        let latest = inheritOrImplementSymbol.type;
        // 基础类型不允许继承或实现
        if (!latest.isType()) {
            this.error(errorMessages.intrinsicTypeDonnotAllowInherit,
                inheritOrImplementSymbol.declaration,
                inheritOrImplementSymbol.declaration.text);
            return;
        }
        let type = signatureSymbol.type;
        //
        if (type.isType()) {
            // 继承
            if (inherits) {
                // interface
                if (signature.isSignature<t.InterfaceSignature>(t.SyntaxKind.interfaceSignature)) {
                    // 接口只能继承接口
                    if ((inheritOrImplementSymbol.kind & t.SymbolKind.interface) === 0) {
                        this.error(errorMessages.implementOrInheritTypeMustBeInterface,
                            inheritOrImplementRange);
                    }
                    else if (checkInheritsOrImplementsDuplication(type, inheritOrImplementSymbol.type, true)) {
                        this.error(errorMessages.interfaceInheritsDuplication,
                            inheritOrImplementRange);
                    }
                    else {
                        (type.inherits || ((type as Mutable<t.Type>).inherits = [])).push(latest);
                    }
                }
                else {
                    // 类只能继承类
                    if ((inheritOrImplementSymbol.kind & t.SymbolKind.class) === 0) {
                        this.error(errorMessages.classImplementsMustBeClass,
                            inheritOrImplementRange);
                    }
                    // 检查class单一继承
                    else if (type.inherits && type.inherits.length === 1) {
                        this.error(errorMessages.classCanOnlyInheritOneBaseClass,
                            inheritOrImplementRange);
                    }
                    else {
                        let latest = inheritOrImplementSymbol.type;
                        if (latest.isType()) {
                            (type.inherits || ((type as Mutable<t.Type>).inherits = [])).push(latest);
                        }
                        else {
                            this.error(errorMessages.intrinsicTypeDonnotAllowInherit, inheritOrImplementRange, latest.name);
                        }
                    }
                }
            }
            // 实现
            else {
                if (signature.isSignature<t.InterfaceSignature>(t.SyntaxKind.interfaceSignature)) {
                    this.error(errorMessages.interfaceCannotImplements, signature.name);
                }
                else {
                    if (checkInheritsOrImplementsDuplication(type, inheritOrImplementSymbol.type, false)) {
                        this.error(errorMessages.classImplementsDuplication, inheritOrImplementRange);
                    }
                    else {
                        (type.implements || ((type as Mutable<t.Type>).implements = [])).push(latest);
                    }
                }
            }
        }
    }

    private checkParameterCompatible(baseParam: t.Symbol, inheritParam: t.Symbol): boolean {
        let result = inheritParam.type.isCompatible(baseParam.type) && inheritParam.checkFlag === baseParam.checkFlag;
        if (!result) {
            this.error(errorMessages.inheritMemberIsNotCompatible,
                inheritParam.declaration.name,
                inheritParam.name);
        }
        return result;
    }

    private checkSymbolCompatible(baseSymbol: t.Symbol, inheritSymbol: t.Symbol): boolean {
        let result = inheritSymbol.type.isCompatible(baseSymbol.type) &&
            baseSymbol.checkFlag === inheritSymbol.checkFlag &&
            baseSymbol.parameters.length === inheritSymbol.parameters.length;
        for (let i = 0; i < inheritSymbol.parameters.length; i++) {
            const inheritParam = inheritSymbol.parameters[i];
            if (i < baseSymbol.parameters.length) {
                this.checkParameterCompatible(baseSymbol.parameters[i], inheritParam);
            }
            else {
                this.error(errorMessages.inheritMemberIsNotCompatible,
                    inheritParam.declaration.name,
                    inheritParam.name);
            }
        }
        if (!result) {
            this.error(errorMessages.inheritMemberIsNotCompatible,
                inheritSymbol.declaration.name,
                inheritSymbol.name);
        }
        return result;
    }

    private checkInheritOrImplementsMemberConflict(baseType: t.LocalType, inheritType: t.LocalType, conflictType: ConflictType) {

        if (!baseType.isType() || !inheritType.isType()) {
            return;
        }

        let baseMembers = baseType.symbol.members;
        let inheritMembers = inheritType.symbol.members;

        // 接口继承或类继承，检查是否有相同的成员的兼容性
        if (conflictType === ConflictType.inheritsInterface || conflictType === ConflictType.inheritsClass) {
            if (!baseMembers || baseMembers.size === 0 || !inheritMembers || inheritMembers.size === 0) {
                return;
            }
            baseMembers.forEach(member => {
                let inheritExist = inheritMembers?.get(member.escapedName);
                if (inheritExist) {
                    this.checkSymbolCompatible(member, inheritExist);
                }
                else {
                    (inheritMembers as Map<string, t.Symbol>)?.set(member.escapedName, member);
                }
            });
        }
        // 类实现，检查是否有缺少的成员声明
        else {
            if (!baseMembers || baseMembers.size === 0) {
                return;
            }
            let lostMembers: string[] = [];
            baseMembers.forEach(v => {
                if (!(inheritMembers && inheritMembers.has(v.escapedName))) {
                    lostMembers.push(v.name);
                }
            });
            if (lostMembers.length > 0) {
                this.error(errorMessages.classImplementsMemberLost, inheritType.symbol.declaration.name, baseType.name, lostMembers.join(","));
            }
        }
    }

    private checkSymbolTypeInheritsOrImplementsConflict(symbol: t.Symbol) {
        const type = symbol.type;
        if (!type.isType()) {
            return;
        }
        if ((symbol.kind & t.SymbolKind.interface) > 0) {
            type.inherits?.forEach(inh => this.checkInheritOrImplementsMemberConflict(inh, type, ConflictType.inheritsInterface));
        }
        else {
            type.inherits?.forEach(inh => this.checkInheritOrImplementsMemberConflict(inh, type, ConflictType.inheritsClass));
            type.implements?.forEach(imp => this.checkInheritOrImplementsMemberConflict(imp, type, ConflictType.implementsInterface));
        }
    }

    private checkClassOrInterfaceInheritsOrImplements(signature: t.InterfaceSignature | t.ClassSignature, scope: t.ReadonlySymbolTableLike, signatureSymbol: t.Symbol) {

        let checkInheritOrImplementElement = (identifiers: t.NodeArray<t.Identifier>, searchScope: t.ReadonlySymbolTableLike, inherits: boolean, needHoldeOn: boolean) => {
            let find = this.getSymbolFromModule(identifiers, signature.module, searchScope, true);
            if (!find) {
                if (needHoldeOn) {
                    this.delay((finalScope) => {
                        const finalFind = checkInheritOrImplementElement(identifiers, finalScope, inherits, false);
                        if (finalFind && finalFind.type.isType() && signatureSymbol.type.isType()) {
                            const isInterface = signature.isSignature<t.InterfaceSignature>(t.SyntaxKind.interfaceSignature);
                            const conflictType =
                                (inherits && isInterface) ? ConflictType.inheritsInterface :
                                (inherits && !isInterface) ? ConflictType.inheritsClass :
                                ConflictType.implementsInterface;
                            this.checkInheritOrImplementsMemberConflict(finalFind.type, signatureSymbol.type, conflictType);
                        }
                    });
                }
            }
            else {
                this.updateClassOrInterfaceInheritsOrImplements(signature, signatureSymbol, find, identifiers, inherits);
            }
            return find;
        };

        signature.inherits?.forEach(inh => checkInheritOrImplementElement(inh.fullName, scope, true, true));

        if (signature.isSignature<t.ClassSignature>(t.SyntaxKind.classSignature)) {
            signature.implements?.forEach(imp => checkInheritOrImplementElement(imp.fullName, scope, false, true));
        }

    }

    private declareClassOrInterface(signature: t.InterfaceSignature | t.ClassSignature, arg: t.ReadonlySymbolTableLike, module?: t.Symbol): t.Symbol {
        const isInterface = signature.isSignature<t.InterfaceSignature>(t.SyntaxKind.interfaceSignature);
        const symbol = this.createSymbol(
            isInterface ? t.SymbolKind.interface : t.SymbolKind.class,
            signature.name.text,
            t.CheckFlag.readonly,
            signature
        );
        const type = this.createObjectType(isInterface ? t.ObjectTypeFlag.interface : t.ObjectTypeFlag.class,
            signature.name.text, symbol);
        symbol.set("parent", module);

        signature.properties.forEach(p => this.declarePropertySignature(p, symbol.members as Map<string, t.Symbol>, symbol));
        signature.methods.forEach(f => this.declareMethodSignature(f, symbol.members as Map<string, t.Symbol>, symbol));
        if (!isInterface) {
            signature.constants.forEach(c => this.declareConstant(c, symbol.members as Map<string, t.Symbol>, symbol));
        }
        if (signature.default) {
            symbol.set("defaultMember", symbol.members.get(signature.default.name.text.toLowerCase()));
        }
        type.members = symbol.members;
        symbol.tracker.setNodeType(signature.name, type);
        this.declareSymbolToSymbolTableLike(symbol,
            arg,
            t.SymbolScope.declare,
            errorMessages.typeRedeclaration
        );
        updateSymbolNode(symbol, signature.name);
        this.checkClassOrInterfaceInheritsOrImplements(signature, arg, symbol);

        // 检查接口成员冲突
        this.checkSymbolTypeInheritsOrImplementsConflict(symbol);

        const meProperty = symbol.members.get(thisPropertyName);
        if (meProperty) {
            if ((meProperty.kind & t.SymbolKind.property) === 0) {
                this.error(errorMessages.interfaceOrClassMeMustBeProperty,
                    meProperty.declaration.name);
            }
            else if (meProperty.parameters.length !== 1) {
                this.error(errorMessages.interfaceOrClassMePropertyRequireOneParameter,
                    meProperty.declaration.name);
            }
            else {
                const parameter = meProperty.parameters[0];
                if (parameter.type === basicTypes.long) {
                    symbol.set("checkFlag", symbol.checkFlag | t.CheckFlag.numberIndex);
                }
                else if (parameter.type === basicTypes.any) {
                    symbol.set("checkFlag", symbol.checkFlag | t.CheckFlag.indexable);
                }
                else {
                    this.error(errorMessages.interfaceOrClassMePropertyTypeError,
                        parameter.declaration);
                }
            }
        }

        return symbol;
    }

    private declareEnumMembers(parentSymbol: t.Symbol, members: readonly t.EnumMemberSignature[]): t.ReadonlySymbolMap {
        const enumsTable = new Map();
        for (const m of members) {
            if (enumsTable.has(m.name.text.toLowerCase())) {
                this.error(errorMessages.enumMemberRedeclaration, m.name, m.name.text);
                continue;
            }
            let elementSymbol = this.createSymbol(t.SymbolKind.enumMember, m.name.text, t.CheckFlag.readonly, m, basicTypes.long);
            elementSymbol.set("parent", parentSymbol);
            elementSymbol.set("initialization", m.value);
            updateSymbolNode(elementSymbol, m.name);
            this.declareSymbolToSymbolTableLike(elementSymbol, enumsTable, t.SymbolScope.declare, errorMessages.enumMemberRedeclaration);
        }
        return enumsTable;
    }

    private declareEnum(signature: t.EnumSignature, scope: t.ReadonlySymbolTableLike, parentSymbol?: t.Symbol): t.Symbol {
        const symbol = this.createSymbol(t.SymbolKind.enum, signature.name.text, t.CheckFlag.readonly, signature);
        const type = this.createObjectType(t.ObjectTypeFlag.enum, symbol.name, symbol);
        const members = this.declareEnumMembers(symbol, signature.members);
        type.members = members;
        symbol.set("parent", parentSymbol);
        updateSymbolNode(symbol, signature.name, type);
        this.declareSymbolToSymbolTableLike(symbol,
            scope,
            t.SymbolScope.declare,
            errorMessages.typeRedeclaration);
        return symbol;
    }

    private declareConstantSignature(signature: t.ConstSignature, scope: t.ReadonlySymbolTableLike, parentSymbol?: t.Symbol): t.Symbol {
        const symbol = this.createSymbol(t.SymbolKind.constant, signature.name.text, t.CheckFlag.readonly, signature);
        symbol.set("parent", parentSymbol);
        updateSymbolNode(symbol, signature.name, this.computeBindingType(signature.bindingType, scope, signature.module));
        this.declareSymbolToSymbolTableLike(symbol,
            scope,
            t.SymbolScope.declare,
            errorMessages.variableReDeclaration);
        return symbol;
    }

    private declareModule(signature: t.ModuleSignature, table: t.ReadonlySymbolMap): t.Symbol {
        // module允许以'.'分隔
        let escapedName = signature.fullName.map(n => n.text).join(".");
        let symbol = this.createSymbol(t.SymbolKind.module, escapedName, t.CheckFlag.readonly, signature);
        let type = this.createObjectType(t.ObjectTypeFlag.module, escapedName, symbol);
        // members
        signature.constants?.forEach(e => this.declareConstantSignature(e, symbol.members, symbol), this);
        signature.classes?.forEach(e => this.declareClassOrInterface(e, symbol.members, symbol), this);
        signature.interfaces?.forEach(e => this.declareClassOrInterface(e, symbol.members, symbol), this);
        signature.functions?.forEach(e => this.declareMethodSignature(e, symbol.members, symbol), this);
        signature.enums?.forEach(e => this.declareEnum(e, symbol.members, symbol), this);
        //
        type.members = symbol.members;
        //// 检查是否已存在同名符号，如果已存在，将当前符号合并进已有符号
        //let exist = table.get(symbol.escapedName);
        //if (exist) {
        //    mergeSymbolTableTo(s => raise(s.declaration.name, errorMessages.moduleMemberRedeclaration, s.name), exist.members, symbol.members);
        //}
        //else {
        //    declareSymbolToTable(symbol, table, true);
        //}
        mergeSymbolMapTo(undefined, signature.locals.table(t.SymbolScope.declare) as t.SymbolMap, symbol.members);
        updateSymbolNode(symbol, signature.name, type);
        this.declareSymbolToSymbolTableLike(symbol, table, t.SymbolScope.declare, params => {
            if (params.existSymbol.is(t.SymbolKind.module)) {
                params.newSymbol.members.forEach(s => {
                    declareSymbolToTable(s, params.existSymbol.members, param => this.error(errorMessages.moduleMemberRedeclaration, param.name, param.name.text));
                });
            }
            updateSymbolNode(params.existSymbol, params.newSymbol.declaration.name);
        });
        return symbol;
    }

    public checkDeclareFile(sourceFile: t.SourceFile, referenceScope?: t.ReadonlySymbolMap): t.ReadonlySymbolMap {
        let scope = sourceFile.locals.table(t.SymbolScope.declare);
        if (referenceScope) {
            (sourceFile as Mutable<t.SourceFile>).locals.reference(referenceScope, t.SymbolScope.declare);
            scope = referenceScope;
        }
        else {
            this.clear();
        }
        this._state.isDeclareFile = true;
        this.onError(d => sourceFile.diagnostics.push(d));
        for (const signature of sourceFile.signatures) {
            switch (signature.kind) {
                case t.SyntaxKind.moduleSignature:
                    this.declareModule(signature as t.ModuleSignature, scope);
                    break;
                case t.SyntaxKind.interfaceSignature:
                case t.SyntaxKind.classSignature:
                    this.declareClassOrInterface(
                        signature.kind === t.SyntaxKind.interfaceSignature ?
                            signature as t.InterfaceSignature :
                            signature as t.ClassSignature,
                        scope);
                        break;
                case t.SyntaxKind.enumSignature:
                    this.declareEnum(signature as t.EnumSignature, scope);
                    break;
                case t.SyntaxKind.functionSignature:
                    this.declareFunctionSignature(signature as t.FunctionSignature, scope);
                    break;
                case t.SyntaxKind.constSignature:
                    this.declareConstantSignature(signature as t.ConstSignature, scope);
                    break;
            }
        }
        if (!referenceScope) {
            this.executeDelayedCallback(scope);
        }
        this.onError();
        this._state.isDeclareFile = false;
        return scope;
    }

    public checkDeclareFiles(files: readonly t.SourceFile[]): t.ReadonlySymbolMap {
        this.clear();
        const scope = new Map<string, t.Symbol>();
        for (const file of files) {
            this.checkDeclareFile(file, scope);
        }
        this.executeDelayedCallback(scope);
        return scope;
    }

    private checkPreprocessorUndefStatement(node: t.PPUndef, scope: t.ReadonlySymbolTableLike) {
        if (node.isMissingNode() || !node.name.text) {
            return;
        }
        if (isSymbolTable(scope)) {
            (scope as t.SymbolTable).delete(node.name.text, t.SymbolScope.macro);
        }
        else {
            (scope as Map<string, t.Symbol>).delete(node.name.text);
        }
    }

    private checkStatement(node: t.Statement, scope: t.ReadonlySymbolTable) {
        if (node.isMissingNode()) {
            return;
        }
        switch (node.kind) {
            case t.SyntaxKind.exprStat:
                this.checkExpressionStatement(node as t.ExprStat, scope);
                break;
            case t.SyntaxKind.block:
                this.checkBlockStatement(node as t.Block, scope);
                break;
            case t.SyntaxKind.jumpStat:
                this.checkJumpStatement(node as t.JumpStatement);
                break;
            case t.SyntaxKind.localDecls:
                this.declareLocalDeclarations(node as t.LocalDecls, scope);
                break;
            case t.SyntaxKind.constDecl:
                this.declareConstant(node as t.ConstantDeclaration, scope);
                break;
            case t.SyntaxKind.funcDecl:
                if (this._state.isSectionStatement) {
                    this.error(errorMessages.functionCannotDeclareInSection,
                        (node as t.FunctionDeclaration).name);
                }
                this.checkFunctionDeclarationBody(node as t.FunctionDeclaration);
                break;
            case t.SyntaxKind.location:
                break;
            case t.SyntaxKind.optionStat:
                this.checkOptionStatement(node as t.OptionStatement);
                break;
            case t.SyntaxKind.ifStat:
                this.checkIfStatement(node as t.IfStat, scope);
                break;
            case t.SyntaxKind.forStat:
                this.checkForStatement(node as t.ForStat, scope);
                break;
            case t.SyntaxKind.forEachStat:
                this.checkForEachStatement(node as t.ForEachStat, scope);
                break;
            case t.SyntaxKind.doStat:
                this.checkDoOrWhileStatement(node as t.DoStat, scope);
                break;
            case t.SyntaxKind.whileStat:
                this.checkDoOrWhileStatement(node as t.WhileStat, scope);
                break;
            case t.SyntaxKind.selectStat:
                this.checkSelectStatement(node as t.SelectStat, scope);
                break;
            case t.SyntaxKind.withStat:
                this.checkWithStatement(node as t.WithStat, scope);
                break;
            case t.SyntaxKind.sectionStat:
                this.checkSectionStatement(node as t.SectionStat, scope);
                break;
            case t.SyntaxKind.eventSection:
                this.checkEventSectionStatement(node as t.EventSection, scope);
                break;
            case t.SyntaxKind.routingSection:
                this.checkRoutingSection(node as t.RoutingSection, scope);
                break;
            case t.SyntaxKind.jobSection:
                this.checkJobSectionStatement(node as t.JobSection, scope);
                break;
            case t.SyntaxKind.inputDataSourceSection:
                this.checkInputDataSourceSection(node as t.InputDataSourceSection, scope);
                break;
            case t.SyntaxKind.outputDataSourceSection:
                this.checkOutputDataSourceSection(node as t.OutputDataSourceSection, scope);
                break;
            case t.SyntaxKind.globalSQLVariablesSection:
                this.checkGlobalSQLVariablesSection(node as t.GlobalSQLVariablesSection, scope);
                break;
            case t.SyntaxKind.loggingSection:
                this.checkLoggingSection(node as t.LoggingSection, scope);
                break;
            case t.SyntaxKind.metadataSection:
                // metadata section 前置声明，提前声明元数据符号
                this.checkMetadataSection(node as t.MetadataSection, scope);
                break;
            // preprocessor
            case t.SyntaxKind.ppDefine:
                if (this._programTable && this._programTable !== scope) {
                    this.declareMacro(node as t.PPDefine, this._programTable);
                }
                this.declareMacro(node as t.PPDefine, scope);
                break;
            case t.SyntaxKind.ppUndef:
                this.checkPreprocessorUndefStatement(node as t.PPUndef, scope);
                break;
            case t.SyntaxKind.ppInclude:
                this.checkPreprocessorIncludeStatement(node as t.PPInclude, scope);
                break;
            case t.SyntaxKind.ppIf:
                this.checkPreprocessorIfStatement(node as t.PPIf, scope);
                break;
            case t.SyntaxKind.ppIfDef:
                this.checkPreprocessorIfDefOrNDefStatement(node as t.PPIfDef, scope);
                break;
            case t.SyntaxKind.ppIfNDef:
                this.checkPreprocessorIfDefOrNDefStatement(node as t.PPIfNDef, scope);
                break;
        }
    }

    private checkExpressionOrStatement(node: t.Expression | t.Statement | undefined, scope: t.ReadonlySymbolTable) {
        if (!node || node.isMissingNode()) {
            return;
        }
        if (node.isExpression()) {
            this.computeExpression(node, scope);
        }
        else {
            this.checkStatement(node, scope);
        }
    }

    private checkExpressionStatement(node: t.ExprStat, scope: t.ReadonlySymbolTable) {
        this.clearContext();
        this.computeExpression(node.expression, scope);
        this.clearContext();
    }

    private checkBlockStatement(node: t.Block, scope: t.ReadonlySymbolTable) {
        for (const stat of node.statements) {
            this.checkExpressionOrStatement(stat, scope);
        }
    }

    private checkJumpStatement(node: t.JumpStatement) {
        if (!node.location || !node.location.isIdentifier()) {
            return;
        }
        if (this._programLocations.length === 0) {
            this.error(errorMessages.locationIsNotExist, node.location, node.location.text);
            return;
        }
        let symbolTable = this._programLocations[this._programLocations.length - 1];
        let declared = symbolTable.get(node.location.text.toLowerCase());
        if (!declared) {
            this.error(errorMessages.locationIsNotExist, node.location, node.location.text);
        }
        else {
            updateSymbolNode(declared, node.location);
        }
    }

    private checkOptionStatement(node: t.OptionStatement) {
        this._optionFlag = node.optionFlag;
    }

    // 函数体有自己的定义域，但是当 Option 为 GlobalVariable 时，允许查找全局变量
    private checkFunctionDeclarationBody(declaration: t.FunctionDeclaration) {
        let parentSymbol = declaration.name.symbol;
        let scope = declaration.locals;
        this._state.isFunctionDeclarationBody = true;
        // 如果函数需要返回值，需要提前声明函数名同名的变量
        if (declaration.hasReturn) {
            const returnSymbol = this.createSymbol(
                t.SymbolKind.variable,
                declaration.name.text,
                t.CheckFlag.none,
                this._converter.convertNodeToDeclaration(declaration.name),
                basicTypes.any
            );
            returnSymbol.set("parent", declaration.symbol);
            returnSymbol.tracker.onNodeTypeChanged((type, _) => {
                if (!parentSymbol) {
                    return;
                }
                let compare = parentSymbol.type.compare(type, true);
                if (compare.success) {
                    parentSymbol.tracker.setNodeType(declaration.name, compare.type);
                }
                else {
                    parentSymbol.tracker.setNodeType(declaration.name, basicTypes.any);
                }
            });
            this.declareSymbolToSymbolTableLike(returnSymbol, scope, t.SymbolScope.local);
        }
        this.startLocationBlock(declaration.body);
        this.checkStatement(declaration.body, scope);
        this.endLocationBlock();
        this._state.isFunctionDeclarationBody = false;
    }

    private checkCondition(condition: t.Expression, scope: t.ReadonlySymbolTable) {
        let symbol = this.computeExpression(condition, scope);
        if (symbol.type.is(t.TypeFlag.void)) {
            this.error(errorMessages.conditionNeedReturnValue, condition);
        }
    }

    private checkIfStatement(node: t.IfStat, scope: t.ReadonlySymbolTable) {
        this.checkCondition(node.condition, scope);
        this.checkExpressionOrStatement(node.then, scope);
        if (node.else) {
            this.checkExpressionOrStatement(node.else, scope);
        }
    }

    private checkForStatement(node: t.ForStat, scope: t.ReadonlySymbolTable) {
        // counter 应当是数字
        if (!node.counter.isMissingNode() && node.counter.text.length > 0) {
            let counter = this.computeIdentifier(node.counter, scope, false);
            updateSymbolNode(counter, node.counter, basicTypes.long);
        }
        this.computeExpression(node.range.lower, scope);
        this.computeExpression(node.range.upper, scope);
        this.checkStatement(node.statement, scope);
    }

    private checkForEachStatement(node: t.ForEachStat, scope: t.ReadonlySymbolTable) {
        let group = this.computeExpression(node.group, scope);
        let element = this.computeIdentifier(node.element, scope, false);
        if (!element.is(t.SymbolKind.undefined)) {
            updateSymbolNode(element, node.element, computeSymbolIterableType(group));
        }
        this.checkStatement(node.statement, scope);
    }

    private checkDoOrWhileStatement(node: t.DoStat | t.WhileStat, scope: t.ReadonlySymbolTable) {
        this.checkCondition(node.expression, scope);
        this.checkStatement(node.statement, scope);
    }

    private checkIterationExpression(expression: t.IterationExpr, scope: t.ReadonlySymbolTable) {
        updateSymbolNode(this.computeExpression(expression.upper, scope), expression.upper);
        updateSymbolNode(this.computeExpression(expression.lower, scope), expression.lower);
    }

    private checkSelectCaseComparison(expression: t.SelectCaseComparison, scope: t.ReadonlySymbolTable) {
        const right = expression.right;
        if (right.isMissingNode()) {
            return;
        }
        if (right.isExpression<t.IterationExpr>(t.SyntaxKind.iteration)) {
            this.checkIterationExpression(right, scope);
        }
        else {
            this.computeExpression(right, scope);
        }
    }

    private checkSelectCaseClause(clause: t.SelectCaseClause, scope: t.ReadonlySymbolTable) {
        for (const condition of clause.conditions) {
            if (condition.isExpression<t.SelectCaseComparison>(t.SyntaxKind.selectCaseComparison)) {
                this.checkSelectCaseComparison(condition, scope);
            }
            else {
                this.computeExpression(condition, scope);
            }
        }
        this.checkStatement(clause.statement, scope);
    }

    private checkSelectStatement(node: t.SelectStat, scope: t.ReadonlySymbolTable) {
        this.checkCondition(node.test, scope);
        for (const clause of node.cases) {
            this.checkSelectCaseClause(clause, scope);
        }
    }

    private checkWithStatement(node: t.WithStat, scope: t.ReadonlySymbolTable) {
        this.computeExpression(node.object, scope);
        this.checkStatement(node.statement, scope);
    }

    // SectionStatement没有自己的定义域，但是不能声明函数
    private checkSectionStatement(node: t.SectionStat, scope: t.ReadonlySymbolTable) {
        const symbol = this.createSymbol(t.SymbolKind.section, node.name.text, t.CheckFlag.readonly, node);
        this.declareSymbolToSymbolTableLike(symbol,
            scope,
            t.SymbolScope.section,
            errorMessages.sectionRedeclare);
        updateSymbolNode(symbol, node.name);
        this._state.isSectionStatement = true;
        this.checkStatement(node.statement, scope);
        this._state.isSectionStatement = false;
    }

    private checkEventSectionStatement(node: t.EventSection, scope: t.ReadonlySymbolTable) {
        const symbol = this.createSymbol(t.SymbolKind.section, node.name.text, t.CheckFlag.readonly, node);
        symbol.set("sectionKind", node.sectionFlag);
        updateSymbolNode(symbol, node.name);
        this.declareSymbolToSymbolTableLike(symbol,
            scope,
            t.SymbolScope.section,
            errorMessages.sectionRedeclare);
        // Event有自己的定义域
        // OnNextCase中，查询元数据符号
        this._state.isOnNextCaseEvent = node.eventSectionFlag === t.EventSectionKind.onNextCase;
        this._state.isEventSection = true;
        this._eventTable = node.locals;
        // Event内，将Event自身的符号表视为全局符号表
        this._globalVariables = node.locals.table(t.SymbolScope.local) as t.SymbolMap;
        // 将全局宏表合并进Event符号表
        node.locals.insert(scope.table(t.SymbolScope.macro), 0);
        // 首先检查函数声明
        for (const f of node.functions) {
            this.declareLocalFunction(f, node.locals);
        }
        // 提前声明 #include 内的函数
        for (const inc of node.includes) {
            let source = inc.targetFile;
            if (!source) {
                continue;
            }
            for (const f of source.declarations) {
                this.declareLocalFunction(f, node.locals);
            }
            forEachSourceFileInclude(source, s => {
                for (const func of s.declarations) {
                    this.declareLocalFunction(func, node.locals);
                }
            });
        }
        this.startLocationBlock(node.statement);
        this.checkStatement(node.statement, node.locals);
        this.endLocationBlock();
        this._eventTable = undefined;
        this._state.isOnNextCaseEvent = false;
        this._state.isEventSection = false;
    }

    private checkRoutingSection(node: t.RoutingSection, scope: t.ReadonlySymbolTable) {
        const symbol = this.createSymbol(t.SymbolKind.section, "Routing", t.CheckFlag.readonly, node);
        symbol.set("sectionKind", t.SectionKind.routing);
        this.startLocationBlock(node.statement);
        this.declareSymbolToSymbolTableLike(symbol, scope, t.SymbolScope.section);
        for (const f of node.functions) {
            this.declareLocalFunction(f, node.locals);
        }
        this.checkStatement(node.statement, node.locals);
        this.endLocationBlock();
    }

    private checkDMSSection<T extends t.Section>(
        section: T,
        sectionKind: t.SectionKind,
        scope: t.ReadonlySymbolTable,
        memberNames: ReadonlySet<string>) {

        const symbol = this.createSymbol(t.SymbolKind.section, section.name.text, t.CheckFlag.readonly, section);
        symbol.set("sectionKind", sectionKind);
        updateSymbolNode(symbol, section.name);
        this.declareSymbolToSymbolTableLike(symbol, scope, t.SymbolScope.section, errorMessages.sectionRedeclare);
        // member
        checkDMSSectionMember(section, member => {
            if (member && !member.isMissingNode() && member.left.isIdentifier()) {
                let lowerName = member.left.text.toLowerCase();
                if (memberNames.has(lowerName)) {
                    const memberSymbol = createSymbolFromNode(member.left, ++this._symbolCount, this._converter);
                    memberSymbol.set("kind", t.SymbolKind.sectionMember);
                    memberSymbol.set("checkFlag", t.CheckFlag.none);
                    memberSymbol.set("parent", symbol);
                    updateSymbolNode(memberSymbol, member.left);
                }
                if (!member.right.isLiteral()) {
                    this.checkExpressionOrStatement(member.right, scope);
                }
            }
        });
    }

    private checkJobSectionStatement(node: t.JobSection, scope: t.ReadonlySymbolTable) {
        this.checkDMSSection(node, t.SectionKind.job, scope, sectionMemberNameSets.job);
    }

    private checkInputDataSourceSection(node: t.InputDataSourceSection, scope: t.ReadonlySymbolTable) {
        this.checkDMSSection(node, t.SectionKind.inputDataSource, scope, sectionMemberNameSets.inputDataSource);
    }

    private checkOutputDataSourceSection(node: t.OutputDataSourceSection, scope: t.ReadonlySymbolTable) {
        this.checkDMSSection(node, t.SectionKind.outputDataSource, scope, sectionMemberNameSets.outputDataSource);
    }

    private checkGlobalSQLVariablesSection(node: t.GlobalSQLVariablesSection, scope: t.ReadonlySymbolTable) {
        this.checkDMSSection(node, t.SectionKind.globalSQLVariables, scope, sectionMemberNameSets.globalSQLVariables);
    }

    private checkLoggingSection(node: t.LoggingSection, scope: t.ReadonlySymbolTable) {
        this.checkDMSSection(node, t.SectionKind.logging, scope, sectionMemberNameSets.logging);
    }

    private checkPreprocessorIncludeStatement(node: t.PPInclude, scope: t.ReadonlySymbolTableLike) {
        if (!node.targetFile) {
            return;
        }
        // 将 node.targetFile 作为声明对象添加到路径字符串的符号中
        const symbol = this.createSymbol(
            t.SymbolKind.sourceFile,
            `"${node.path.text.replace(/[\\]/g, "/")}"`,
            t.CheckFlag.readonly,
            node.targetFile
        );
        updateSymbolNode(symbol, node.path);
        const state = this.saveState();
        this.onError(d => node.targetFile?.diagnostics.push(d));
        if (this._state.isMetadata && node.targetFile.metadata) {
            // 将元数据声明到全局元数据符号表中
            this.checkMetadataDocument(node.targetFile.metadata, scope);
        }
        else if (!this._state.isMetadata) {
            this.updateCommentFlag(node.targetFile);
            // 对于 #include ，由于函数声明已经前置声明，此处只检查普通语句
            const sourceScope = node.targetFile.locals;
            if (isSymbolTable(scope)) {
                sourceScope.insert(scope, 0);
            }
            else {
                sourceScope.insert(scope, 0);
            }
            for (const stat of node.targetFile.statements) {
                this.checkStatement(stat, sourceScope);
            }
            if (isSymbolTable(scope)) {
                (scope as t.SymbolTable).insert(sourceScope, node.end);
            }
            else {
                sourceScope.forEach(
                    s => (scope as t.SymbolMap).set(s.escapedName, s),
                    t.SymbolScope.local | t.SymbolScope.function | t.SymbolScope.macro
                );
            }
        }
        this.recoveryState(state);
    }

    private checkPreprocessorIfStatement(node: t.PPIf, scope: t.ReadonlySymbolTable) {
        this._state.isPreprocessorIfStatementCondition = true;
        const condition = this.computeExpression(node.condition, scope);
        this._state.isPreprocessorIfStatementCondition = false;
        if (condition.type.flag === t.TypeFlag.void) {
            this.error(errorMessages.conditionNeedReturnValue, node.condition);
        }
        this.checkStatement(node.then, scope);
        if (node.else) {
            this.checkStatement(node.else, scope);
        }
    }

    private checkPreprocessorIfDefOrNDefStatement(node: t.PPIfDef | t.PPIfNDef, scope: t.ReadonlySymbolTable) {
        // 对当前宏进行声明判断，如果已声明，检查语句块内容，否则不检查
        if (node.macroName.isMissingNode()) {
            return;
        }
        let exist = this.getSymbol(node.macroName.text, scope, t.SymbolScope.macro);
        // 如果node是#ifdef且未声明或者node是#ifndef且已声明，跳过当前条件，如果有else，检查else语句块内容
        if ((node.isPreprocessor<t.PPIfDef>(t.SyntaxKind.ppIfDef) && !exist) ||
            (node.isPreprocessor<t.PPIfNDef>(t.SyntaxKind.ppIfNDef) && exist)
        ) {
            if (node.else && !node.else.isPreprocessor<t.PPIf>(t.SyntaxKind.ppIf)) {
                this.checkStatement(node.else, scope);
            }
            return;
        }
        updateSymbolNode(exist, node.macroName);
        this.checkStatement(node.then, scope);
        // 当声明判断通过时，剩余的分支条件不会触发，直接跳过即可。
    }

    // metadata

    private startMetadataReadonlySymbol(typeName: string, symbolName: string, declaration: t.Declaration, metadataKind: t.SymbolMDMKind, parent?: t.Symbol): t.Symbol {
        const type = this.getSymbol(typeName, undefined, t.SymbolScope.declare)?.type || basicTypes.object;
        const symbol = this.createSymbol(t.SymbolKind.metadata, symbolName, t.CheckFlag.readonly, declaration, type);
        symbol.set("mdmKind", metadataKind);
        symbol.set("parent", parent);
        updateSymbolNode(symbol, declaration.name);
        return symbol;
    }

    private computeMacroValue(macro: t.PPDefine): string {
        return macro.valueText;
    }

    private checkMetadataExpressionLikeValue<T>(expr: t.Expression | undefined, scope: t.ReadonlySymbolTableLike, checker: (str: string) => T | undefined, errorMessage: DiagnosticMessage, defaultValue: T): T {
        if (!expr ||
            expr.isMissingNode() ||
            expr.isExpression<t.EmptyExpr>(t.SyntaxKind.emptyExpr) ||
            expr.text.length === 0) {
            return defaultValue;
        }
        let text = removeStringQuotation(expr.text);
        if (expr.isIdentifier()) {
            let macro = isSymbolTable(scope) ? scope.get(text, t.SymbolScope.macro) : scope.get(text);
            if (macro &&
                macro.is(t.SymbolKind.macro) &&
                macro.declaration.isPreprocessor<t.PPDefine>(t.SyntaxKind.ppDefine)
            ) {
                text = removeStringQuotation(this.computeMacroValue(macro.declaration));
                updateSymbolNode(macro, expr);
            }
        }
        let result = checker(text);
        if (result === undefined) {
            this.error(errorMessage, expr, text);
        }
        return result ?? defaultValue;
    }

    private checkMetadataAreaLikeMembers(
        obj: { language?: t.Expression, context?: t.Expression, labelType?: t.Expression },
        document: { language: LocalLanguage, context: t.MDMUserContextFlags, labelType: t.MDMLabelTypeFlags },
        scope: t.ReadonlySymbolTableLike): {
        language: LocalLanguage,
        context: t.MDMUserContextFlags,
        labelType: t.MDMLabelTypeFlags,
    } {
        const language = this.checkMetadataExpressionLikeValue(
            obj.language,
            scope,
            str => longCodeTextToMetadataLanguage(str) || shortCodeTextToMetadataLanguage(str),
            errorMessages.invalidMetadataLanguage,
            document.language);

        const context = this.checkMetadataExpressionLikeValue(
            obj.context,
            scope,
            stringToUserContext,
            errorMessages.invalidMetadataUserContext,
            document.context);

        const labelType = this.checkMetadataExpressionLikeValue(
            obj.labelType,
            scope,
            stringToLabelType,
            errorMessages.invalidMetadataLabelType,
            document.labelType);

        return { language, context, labelType };
    }

    private declareMetadataAreaName(areaName: t.MDMAreaName, document: t.MDMDocument, scope: t.ReadonlySymbolMap) {
        const symbol = this.createSymbol(t.SymbolKind.metadata, areaName.name.text, t.CheckFlag.none, areaName, basicTypes.object);
        symbol.set("mdmKind", t.SymbolMDMKind.areaName);
        this.checkMetadataAreaLikeMembers(areaName, document, scope);
        this.declareSymbolToSymbolTableLike(
            symbol,
            scope,
            t.SymbolScope.metadata,
            errorMessages.duplicateMetadataCategory
        );
        updateSymbolNode(symbol, areaName.name);
        return symbol;
    }

    private checkMetadataArealObject(obj: t.MDMArealBase, table: t.ReadonlySymbolMap) {
        if (!obj.areaName) {
            return;
        }
        const areaName = table.get(obj.areaName.text.toLowerCase());
        if (areaName && areaName.isMDM(t.SymbolMDMKind.areaName)) {
            updateSymbolNode(areaName, obj.areaName);
        }
        else {
            this.error(errorMessages.unknownAreaName, obj.areaName, obj.areaName.text);
        }
    }

    private declareMetadataProperty(property: t.MDMProperty, table: t.ReadonlySymbolMap, parent: t.Symbol) {
        if (property.name.isMissingNode()) {
            return;
        }
        const symbol = this.createSymbol(
            t.SymbolKind.metadata,
            property.name.text,
            t.CheckFlag.none,
            property,
            basicTypes.object
        );
        symbol.set("mdmKind", t.SymbolMDMKind.property);
        symbol.set("initialization", tryGetMetadataValue(property.value));
        symbol.set("parent", parent);
        updateSymbolNode(symbol, property.name);
        declareSymbolToTable(symbol, table);
    }

    private declareMetadataProperties(properties: t.MDMProperties, table: t.ReadonlySymbolMap, parent: t.Symbol) {
        const symbol = this.startMetadataReadonlySymbol(
            "MDM.IProperties",
            `${properties.areaName ? `${properties.areaName.text}:` : ""}Properties`,
            properties,
            t.SymbolMDMKind.properties,
            parent
        );
        // check areal name
        this.checkMetadataArealObject(properties, table);
        // declare members
        properties.forEachItem(prop => this.declareMetadataProperty(prop, symbol.members, symbol));
        properties.forEachObject(obj => this.declareMetadataProperties(obj, table, parent));
        return symbol;
    }

    private declareMetadataTemplate(template: t.MDMTemplate, table: t.ReadonlySymbolMap, parent: t.Symbol) {
        const symbol = this.createSymbol(
            t.SymbolKind.metadata,
            template.name.text,
            t.CheckFlag.readonly,
            template,
            basicTypes.object
        );
        symbol.set("mdmKind", t.SymbolMDMKind.template);
        symbol.set("parent", parent);
        this.declareSymbolToSymbolTableLike(symbol, table, t.SymbolScope.declare,errorMessages.propertyRedeclaration);
        updateSymbolNode(symbol, template.name);
        return symbol;
    }

    private declareMetadataTemplates(templates: t.MDMTemplates, table: t.ReadonlySymbolMap, parent: t.Symbol) {
        const symbol = this.createSymbol(
            t.SymbolKind.metadata,
            `${templates.areaName ? templates.areaName.text + ":" : ""}Templates`,
            t.CheckFlag.readonly,
            templates,
            basicTypes.object
        );
        symbol.set("mdmKind", t.SymbolMDMKind.templates);
        symbol.set("parent", parent);
        this.checkMetadataArealObject(templates, table);
        templates.forEachItem(template => this.declareMetadataTemplate(template, symbol.members, symbol));
        templates.forEachObject(t => this.declareMetadataTemplates(t, table, parent));
        this.declareSymbolToSymbolTableLike(symbol, table, t.SymbolScope.metadata);
        updateSymbolNode(symbol, templates.name);
        return symbol;
    }

    private declareStyleProperty(property: t.MDMProperty | undefined, table: t.ReadonlySymbolMap, parent: t.Symbol) {
        if (property && !property.isMissingNode() && !property.name.isMissingNode()) {
            this.declareMetadataProperty(property, table, parent);
        }
    }

    private declareMetadataFontStyle(fontStyle: t.MDMFontStyle, table: t.ReadonlySymbolMap, parent: t.Symbol) {
        const symbol = this.startMetadataReadonlySymbol(
            metadataUsingTypeNames.fontStyle,
            "FontStyle",
            fontStyle,
            t.SymbolMDMKind.fontStyle,
            parent
        );
        // declare members
        this.declareStyleProperty(fontStyle.family, symbol.members, symbol);
        this.declareStyleProperty(fontStyle.size, symbol.members, symbol);
        this.declareStyleProperty(fontStyle.isUnderline, symbol.members, symbol);
        this.declareStyleProperty(fontStyle.isItalic, symbol.members, symbol);
        this.declareStyleProperty(fontStyle.isBold, symbol.members, symbol);
        this.declareStyleProperty(fontStyle.isStrikethrough, symbol.members, symbol);
        this.declareStyleProperty(fontStyle.isOverline, symbol.members, symbol);
        this.declareStyleProperty(fontStyle.isBlink, symbol.members, symbol);
        this.declareStyleProperty(fontStyle.isSuperscript, symbol.members, symbol);
        this.declareStyleProperty(fontStyle.isSubscript, symbol.members, symbol);

        this.declareSymbolToSymbolTableLike(symbol, table, t.SymbolScope.metadata);
        updateSymbolNode(symbol, fontStyle.name);

        return symbol;
    }

    private declareMetadataControlStyle(controlStyle: t.MDMControlStyle, table: t.ReadonlySymbolMap, parent: t.Symbol) {
        const symbol = this.startMetadataReadonlySymbol(
            metadataUsingTypeNames.controlStyle,
            "ControlStyle",
            controlStyle,
            t.SymbolMDMKind.controlStyle,
            parent
        );
        // declare members
        this.declareStyleProperty(controlStyle.accelerator, symbol.members, symbol);
        this.declareStyleProperty(controlStyle.type, symbol.members, symbol);
        this.declareStyleProperty(controlStyle.readOnly, symbol.members, symbol);

        this.declareSymbolToSymbolTableLike(symbol, table, t.SymbolScope.metadata);
        updateSymbolNode(symbol, controlStyle.name);

        return symbol;
    }

    private declareMetadataCellStyle(cellStyle: t.MDMCellStyle, table: t.ReadonlySymbolMap, parent: t.Symbol) {
        const symbol = this.startMetadataReadonlySymbol(
            metadataUsingTypeNames.cellStyle,
            "CellStyle",
            cellStyle,
            t.SymbolMDMKind.cellStyle,
            parent
        );
        // declare members
        this.declareStyleProperty(cellStyle.width, symbol.members, symbol);
        this.declareStyleProperty(cellStyle.height, symbol.members, symbol);
        this.declareStyleProperty(cellStyle.borderColor, symbol.members, symbol);
        this.declareStyleProperty(cellStyle.borderTopColor, symbol.members, symbol);
        this.declareStyleProperty(cellStyle.borderRightColor, symbol.members, symbol);
        this.declareStyleProperty(cellStyle.borderBottomColor, symbol.members, symbol);
        this.declareStyleProperty(cellStyle.borderLeftColor, symbol.members, symbol);
        this.declareStyleProperty(cellStyle.borderStyle, symbol.members, symbol);
        this.declareStyleProperty(cellStyle.borderWidth, symbol.members, symbol);
        this.declareStyleProperty(cellStyle.borderTopWidth, symbol.members, symbol);
        this.declareStyleProperty(cellStyle.borderRightWidth, symbol.members, symbol);
        this.declareStyleProperty(cellStyle.borderBottomWidth, symbol.members, symbol);
        this.declareStyleProperty(cellStyle.borderLeftWidth, symbol.members, symbol);
        this.declareStyleProperty(cellStyle.borderTopStyle, symbol.members, symbol);
        this.declareStyleProperty(cellStyle.borderRightStyle, symbol.members, symbol);
        this.declareStyleProperty(cellStyle.borderBottomStyle, symbol.members, symbol);
        this.declareStyleProperty(cellStyle.borderLeftStyle, symbol.members, symbol);
        this.declareStyleProperty(cellStyle.padding, symbol.members, symbol);
        this.declareStyleProperty(cellStyle.paddingTop, symbol.members, symbol);
        this.declareStyleProperty(cellStyle.paddingRight, symbol.members, symbol);
        this.declareStyleProperty(cellStyle.paddingBottom, symbol.members, symbol);
        this.declareStyleProperty(cellStyle.paddingLeft, symbol.members, symbol);
        this.declareStyleProperty(cellStyle.wrap, symbol.members, symbol);
        this.declareStyleProperty(cellStyle.colSpan, symbol.members, symbol);
        this.declareStyleProperty(cellStyle.rowSpan, symbol.members, symbol);
        this.declareStyleProperty(cellStyle.bgColor, symbol.members, symbol);
        this.declareStyleProperty(cellStyle.repeatHeader, symbol.members, symbol);
        this.declareStyleProperty(cellStyle.repeatSideHeader, symbol.members, symbol);

        this.declareSymbolToSymbolTableLike(symbol, table, t.SymbolScope.metadata);
        updateSymbolNode(symbol, cellStyle.name);

        return symbol;
    }

    private declareMetadataStyle(style: t.MDMStyle, table: t.ReadonlySymbolMap, parent: t.Symbol) {
        const symbol = this.startMetadataReadonlySymbol(
            metadataUsingTypeNames.style,
            `${style.areaName ? `${style.areaName.text}:` : ""}Style`,
            style,
            t.SymbolMDMKind.style,
            parent
        );
        // areal
        this.checkMetadataArealObject(style, table);
        // declare members
        if (style.font) {
            this.declareMetadataFontStyle(style.font, symbol.members, symbol);
        }
        if (style.cell) {
            this.declareMetadataCellStyle(style.cell, symbol.members, symbol);
        }
        if (style.control) {
            this.declareMetadataControlStyle(style.control, symbol.members, symbol);
        }
        this.declareStyleProperty(style.color, symbol.members, symbol);
        this.declareStyleProperty(style.bgColor, symbol.members, symbol);
        this.declareStyleProperty(style.hidden, symbol.members, symbol);
        this.declareStyleProperty(style.align, symbol.members, symbol);
        this.declareStyleProperty(style.verticalAlign, symbol.members, symbol);
        this.declareStyleProperty(style.elementAlign, symbol.members, symbol);
        this.declareStyleProperty(style.indent, symbol.members, symbol);
        this.declareStyleProperty(style.zIndex, symbol.members, symbol);
        this.declareStyleProperty(style.cursor, symbol.members, symbol);
        this.declareStyleProperty(style.image, symbol.members, symbol);
        this.declareStyleProperty(style.imagePosition, symbol.members, symbol);
        this.declareStyleProperty(style.orientation, symbol.members, symbol);
        this.declareStyleProperty(style.columns, symbol.members, symbol);
        this.declareStyleProperty(style.rows, symbol.members, symbol);
        this.declareStyleProperty(style.width, symbol.members, symbol);
        this.declareStyleProperty(style.height, symbol.members, symbol);
        // declare area objects
        style.forEachObject(obj => this.declareMetadataStyle(obj, table, parent));
        this.declareSymbolToSymbolTableLike(symbol, table, t.SymbolScope.metadata);
        updateSymbolNode(symbol, style.name);
        return symbol;
    }

    private startMetadataLabeledObject(typeName: string, declaration: t.MDMLabeledObject, flag: t.SymbolMDMKind, parent?: t.Symbol) {
        const type = this.getSymbol(typeName, undefined, t.SymbolScope.declare)?.type || basicTypes.object;
        const symbol = this.createSymbol(t.SymbolKind.metadata, declaration.name.text, t.CheckFlag.readonly, declaration, type);
        symbol.set("mdmKind", flag);
        symbol.set("parent", parent);
        // properties
        if (declaration.properties) {
            this.declareMetadataProperties(declaration.properties, symbol.members, symbol);
        }
        // templates
        if (declaration.templates) {
            this.declareMetadataTemplates(declaration.templates, symbol.members, symbol);
        }
        // style
        if (declaration.style) {
            this.declareMetadataStyle(declaration.style, symbol.members, symbol);
        }
        // labelstyle
        if (declaration.labelStyle) {
            this.declareMetadataStyle(declaration.labelStyle, symbol.members, symbol);
        }
        return symbol;
    }

    private declareMetadataDeclarationInitialization(declaration: t.MDMDeclaration | undefined,
        flag: t.SymbolMDMKind,
        parent: t.Symbol,
        initialization?: string | boolean | number) {
        if (!declaration) {
            return;
        }
        const symbol = this.createSymbol(
            t.SymbolKind.metadata,
            declaration.name.text,
            t.CheckFlag.readonly,
            declaration,
            basicTypes.object
        );
        symbol.set("parent", parent);
        symbol.set("mdmKind", flag);
        if (initialization !== undefined) {
            symbol.set("initialization", initialization);
        }
        updateSymbolNode(symbol, declaration.name);
        this.declareSymbolToSymbolTableLike(symbol, parent.members, t.SymbolScope.metadata);
        return symbol;
    }

    private declareMetadataCategoryExpression(expression: t.MDMCategoryExpression | undefined, parent: t.Symbol) {
        return expression ?
            this.declareMetadataDeclarationInitialization(
                expression,
                t.SymbolMDMKind.expression,
                parent,
                expression.value.text
            ) :
            undefined;
    }

    private declareMetadataCategoryFactor(factor: t.MDMFactor | undefined, parent: t.Symbol) {
        return factor ?
            this.declareMetadataDeclarationInitialization(
                factor,
                t.SymbolMDMKind.factor,
                parent,
                factor.value.text
            ) :
            undefined;
    }

    private declareMetadataCategoryElementType(element: t.MDMCategoryElementType | undefined, parent: t.Symbol) {
        return element ?
            this.declareMetadataDeclarationInitialization(
                element,
                t.SymbolMDMKind.elementType,
                parent,
                element.elementType.text
            ) :
            undefined;
    }

    private declareMetadataUsageType(usage: t.MDMUsageType | undefined, parent: t.Symbol) {
        return usage ?
            this.declareMetadataDeclarationInitialization(
                usage,
                t.SymbolMDMKind.usageType,
                parent,
                usage.value.text
            ):
            undefined;
    }

    private declareMetadataCategoryMultplier(multiplier: t.MDMMultiplier | undefined, parent: t.Symbol) {
        return multiplier ?
            this.declareMetadataDeclarationInitialization(
                multiplier,
                t.SymbolMDMKind.multiplier,
                parent,
                multiplier.field?.text
            ):
            undefined;
    }

    private declareMetadataCategoryOther(other: t.MDMOther | undefined, parent: t.Symbol) {
        return other ?
            this.declareMetadataDeclarationInitialization(
                other,
                t.SymbolMDMKind.other,
                parent,
                other.field?.text
            ):
            undefined;
    }

    private declareMetadataCategorySubList(sublist: t.MDMSublist | undefined, parent: t.Symbol) {
        if (!sublist) {
            return;
        }
        const symbol = this.createSymbol(
            t.SymbolKind.metadata,
            "sublist",
            t.CheckFlag.none,
            sublist,
            basicTypes.object
        );
        symbol.set("parent", parent);
        symbol.set("mdmKind", t.SymbolMDMKind.sublist);
        this.declareSymbolToSymbolTableLike(symbol, parent.members, t.SymbolScope.metadata);
        return symbol;
    }

    private declareMetadataCategory(category: t.MDMCategory, table: t.ReadonlySymbolMap, parent: t.Symbol) {
        const symbol = this.startMetadataLabeledObject(
            metadataUsingTypeNames.category,
            category,
            t.SymbolMDMKind.element,
            parent
        );
        // check name is '-'
        if (category.name.text === "-") {
            let replaceName = "";
            if (category.na) {
                replaceName = "NA";
            }
            if (category.ref) {
                replaceName = "REF";
            }
            if (category.dk) {
                replaceName = "DK";
            }
            if (replaceName.length === 0) {
                if (category.other) {
                    replaceName = "other";
                }
                else if (category.multiplier) {
                    replaceName = "mutiplier";
                }
                else {
                    // 尝试将标签转换成名称
                    let label = category.label?.label;
                    if (label) {
                        if (isLigalIdentifier(label.text)) {
                            replaceName = label.text;
                        }
                        else {
                            this.error(errorMessages.illegalMetadataName, label, label.text);
                        }
                    }
                }
            }
            if (replaceName.length > 0) {
                (symbol as Mutable<t.Symbol>).name = replaceName;
                (symbol as Mutable<t.Symbol>).escapedName = replaceName.toLowerCase();
            }
            // 非法符号，不声明
            else {
                return symbol;
            }
        }
        // declare members
        this.declareMetadataCategoryExpression(category.expression, symbol);
        this.declareMetadataCategoryElementType(category.elementType, symbol);
        this.declareMetadataCategoryFactor(category.factor, symbol);
        this.declareMetadataCategoryMultplier(category.multiplier, symbol);
        this.declareMetadataCategoryOther(category.other, symbol);
        this.declareMetadataCategorySubList(category.sublist, symbol);
        if (category.categories) {
            this.declareMetadataCategories(category.categories, symbol.locals, symbol);
        }
        // use list
        if (category.list) {
            const listSymbol = this.getSymbol(category.list.text, table, t.SymbolScope.metadata);
            if (listSymbol) {
                updateSymbolNode(listSymbol, category.list);
                // 将所有列表已声明的Category对象声明到列表中
                let redeclarations: string[] = [];
                listSymbol.locals.forEach(cat => {
                    this.declareSymbolToSymbolTableLike(cat, table, t.SymbolScope.metadata, param => redeclarations.push(param.name.text));
                });
                if (redeclarations.length > 0) {
                    this.error(errorMessages.duplicateMetadataCategory, category.list, redeclarations.join(","));
                }
            }
            else {
                this.error(errorMessages.unknownMetadataCategories, category.list, category.list.text);
            }
            // 将 list_name 是为声明名称
            if (category.listName) {
                updateSymbolNode(symbol, category.listName);
            }
        }
        else {
            updateSymbolNode(symbol, category.name);
        }
        this.declareSymbolToSymbolTableLike(symbol, table, t.SymbolScope.metadata, errorMessages.duplicateMetadataCategory);
        return symbol;
    }

    private declareMetadataCategories(categories: t.MDMCategories, table: t.ReadonlySymbolMap, parent: t.Symbol) {
        categories.forEachElement(category => this.declareMetadataCategory(category, table, parent));
    }

    private declareMetadataDeclarationValueLike<F extends t.MDMDeclarationKind, V>(node: t.MDMDeclarationValueLike<F, V> | undefined, parent: t.Symbol, metadataFlag: t.SymbolMDMKind) {
        if (!node) {
            return;
        }
        const symbol = this.createSymbol(
            t.SymbolKind.metadata,
            node.name.text,
            t.CheckFlag.none,
            node,
        );
        symbol.set("parent", parent);
        symbol.set("mdmKind", metadataFlag);
        updateSymbolNode(symbol, node.name);
    }

    private declareMetadataField(field: t.MDMField, scope: t.ReadonlySymbolMap, parent?: t.Symbol) {
        const metadataFlag = field.fieldFlag === t.MDMFieldFlags.define ? t.SymbolMDMKind.categories : t.SymbolMDMKind.field;
        const typeName = field.fieldFlag === t.MDMFieldFlags.define ? metadataUsingTypeNames.categories : metadataUsingTypeNames.field;
        const symbol = this.startMetadataLabeledObject(typeName, field, metadataFlag, parent);
        // page
        if (field.fieldFlag === t.MDMFieldFlags.page) {
            const page = field as t.MDMPage;
            // field names
            for (const fieldName of page.questionNames) {
                const question = this.getSymbol(fieldName.text, scope, t.SymbolScope.metadata);
                if (question) {
                    updateSymbolNode(question, fieldName);
                }
                else {
                    this.error(errorMessages.doNotFindMetadataPageItem, fieldName, fieldName.text);
                }
            }
        }
        else {
            // categories
            this.declareMetadataCategories(field.categories, symbol.locals, symbol);
            // subfield
            if (field.class) {
                if (field.class.name && !field.class.name.isMissingNode()) {
                    const classSymbol = this.createSymbol(
                        t.SymbolKind.metadata,
                        "@class",
                        t.CheckFlag.none,
                        field.class
                    );
                    classSymbol.set("parent", symbol);
                    classSymbol.set("mdmKind", t.SymbolMDMKind.class);
                    updateSymbolNode(classSymbol, field.class.name);
                }
                for (const type of field.class.types) {
                    this.declareMetadataField(type, symbol.members, symbol);
                }
                for (const f of field.class.fields) {
                    this.declareMetadataField(f, symbol.members, symbol);
                }
                for (const page of field.class.pages) {
                    this.declareMetadataField(page, symbol.members, symbol);
                }
            }
            // usage type
            this.declareMetadataUsageType(field.usageType, symbol);
        }
        updateSymbolNode(symbol, field.name);
        this.declareSymbolToSymbolTableLike(symbol,
            scope,
            t.SymbolScope.metadata,
            param => {
                mergeMetadataFields(param.existSymbol, param.newSymbol, (n, e) => {
                    this.error(errorMessages.fieldIsNotCompatibleWithExistField, n.name, n.name.text, metadataFieldFlagToString(e.fieldFlag));
                });
            }
        );
        // 后缀
        this.declareMetadataDeclarationValueLike(field.precision, symbol, t.SymbolMDMKind.precision);
        this.declareMetadataDeclarationValueLike(field.scale, symbol, t.SymbolMDMKind.scale);
        this.declareMetadataDeclarationValueLike(field.validation, symbol, t.SymbolMDMKind.validation);
        this.declareMetadataDeclarationValueLike(field.expression, symbol, t.SymbolMDMKind.expression);
        this.declareMetadataDeclarationValueLike(field.initialAnswer, symbol, t.SymbolMDMKind.initialAnswer);
        this.declareMetadataDeclarationValueLike(field.defaultAnswer, symbol, t.SymbolMDMKind.defaultAnswer);
        this.declareMetadataDeclarationValueLike(field.axis, symbol, t.SymbolMDMKind.axis);
        // codes
        if (field.codes) {
            if (field.codes.useField) {
                let targetFieldName = field.codes.useField;
                let targetField = this.getSymbol(targetFieldName.text, scope, t.SymbolScope.metadata);
                if (!targetField || !targetField.isMDM(t.SymbolMDMKind.field)) {
                    this.error(errorMessages.unknownMetadataField, targetFieldName, targetFieldName.text);
                }
                else {
                    updateSymbolNode(targetField, targetFieldName);
                    let targetFieldNode = targetField.declaration;
                    if (targetFieldNode.isMDMField()) {
                        targetFieldNode.categories.forEachElement(element => {
                            (field.codes!.value as t.MutableNodeArray<t.MDMCategory>).push(element);
                        });
                    }
                }
            }
            else {
                for (const node of field.codes.value) {
                    this.declareMetadataCategory(node, symbol.locals, symbol);
                }
            }
        }

        return symbol;
    }

    public checkMetadataDocument(document: t.MDMDocument, scope: t.ReadonlySymbolTableLike): t.ReadonlySymbolMap {
        const symbolTable = document.locals.table(t.SymbolScope.metadata);
        const metadatas = isSymbolTable(scope) ? scope.table(t.SymbolScope.metadata) : scope;
        if (document.preprocessor) {
            for (const node of document.preprocessor) {
                if (node.isPreprocessor<t.PPInclude>(t.SyntaxKind.ppInclude) &&
                    node.targetFile
                ) {
                    this.checkPreprocessorIncludeStatement(node, scope);
                }
            }
        }
        for (const area of document.lcl) {
            this.declareMetadataAreaName(area, document, symbolTable);
        }
        for (const type of document.types) {
            this.declareSymbolToSymbolTableLike(this.declareMetadataField(type, metadatas), symbolTable, t.SymbolScope.metadata);
        }
        for (const field of document.fields) {
            this.declareSymbolToSymbolTableLike(this.declareMetadataField(field, metadatas), symbolTable, t.SymbolScope.metadata);
        }
        return symbolTable;
    }

    private checkMetadataSection(metadata: t.MetadataSection, scope: t.ReadonlySymbolTableLike) {
        const symbol = this.createSymbol(t.SymbolKind.section,
            "Metadata",
            t.CheckFlag.readonly,
            metadata,
            basicTypes.object);
        this.checkMetadataAreaLikeMembers(metadata, {
            language: this._options.defaultMetadataLanguage,
            context: this._options.defaultMetadataContext,
            labelType: this._options.defaultMetadataLabelType
        }, scope);
        // datasource
        if (metadata.dataSource) {
            this.delay(() => {
                let ds = metadata.dataSource!.text;
                let declared = isSymbolTable(scope) ? scope.get(ds, t.SymbolScope.section) : scope.get(ds.toLowerCase());
                if (declared && declared.isSection(t.SectionKind.inputDataSource)) {
                    updateSymbolNode(declared, metadata.dataSource!);
                }
                else {
                    this.error(errorMessages.inputDataSourceIsNotDeclared, metadata.dataSource!, ds);
                }
            });
        }
        // document 由于已经提前声明，此处不在继续检查符号
        // this.checkMetadataDocument(metadata.document, scope);
        // preprocessor
        // if (metadata.document.preprocessor) {
        //     for (const pre of metadata.document.preprocessor) {
        //         if (!pre.isPreprocessor<PreprocessorIncludeStatement>(SyntaxFlag.preprocessorIncludeStatement)) {
        //             continue;
        //         }
        //         this.checkPreprocessorIncludeStatement(pre, scope);
        //     }
        // }
        return symbol;
    }

    public checkSourceFile(source: t.SourceFile): t.ReadonlySymbolTable {
        const scope = source.locals;
        this._programTable = scope;
        this.onError(d => source.diagnostics.push(d));
        this.clear();
        this.selectUsingModule(source.fileKind);
        this.updateCommentFlag(source);
        // 元数据文件
        if (source.metadata && source.fileKind === t.FileKind.mdd) {
            this.checkMetadataDocument(source.metadata, scope);
            return scope;
        }
        // 首先声明本地函数
        for (const fun of source.declarations) {
            this.declareLocalFunction(fun, scope);
        }
        // 对于引入的文件，提前声明函数
        forEachSourceFileInclude(source, (s) => {
            for (const func of s.declarations) {
                this.declareLocalFunction(func, scope);
            }
        });
        // 提前声明元数据对象
        for (const section of source.sections) {
            if (section.isStatement<t.MetadataSection>(t.SyntaxKind.metadataSection)) {
                this._state.isMetadata = true;
                this.checkMetadataDocument(section.document, scope);
                this._state.isMetadata = false;
            }
        }
        // 提前声明行标签
        this.startLocationBlock(source);
        // 检查本地语句
        for (const stat of source.statements) {
            this.checkStatement(stat, scope);
        }
        this.endLocationBlock();
        this.executeDelayedCallback(scope);
        // 添加未核对符号的宏, `SourceFile.macroNodes`中的节点, 都已经在解析节点区分过是否声明,
        // 如果未声明, 在解析时不会添加进此列表。
        for (const macro of source.macroNodes) {
            let name = macro.isIdentifier() ? macro : (macro.callee as t.Identifier);
            if (name.symbol) {
                continue;
            }
            // 此处不报错
            let symbol = scope.positionAt(name.start).find(e => e.escapedName === name.text);
            updateSymbolNode(symbol, name);
        }
        this._usingModule.splice(0);
        this._programTable = undefined;
        return scope;
    }

}
